You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/internal/provider/aggregations/rainbow.go

177 lines
3.7 KiB
Go

package aggregations
import (
"context"
"fmt"
"net/http"
"net/url"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"github.com/zijiren233/go-uhc"
)
var _ provider.AggregationProviderInterface = (*Rainbow)(nil)
const DefaultRainbowAPI = "https://u.cccyun.cc"
type Rainbow struct {
api string
}
func (r *Rainbow) SetAPI(api string) {
r.api = api
}
func (r *Rainbow) Provider() provider.OAuth2Provider {
return "rainbow"
}
func (r *Rainbow) Providers() []provider.OAuth2Provider {
return []provider.OAuth2Provider{
"qq",
"wx",
"alipay",
"baidu",
"microsoft",
}
}
type rainbowGenericProvider struct {
parent *Rainbow
t string
conf provider.Oauth2Option
}
func (r *Rainbow) newGenericProvider(t string) provider.Interface {
return &rainbowGenericProvider{
parent: r,
t: t,
}
}
func (p *rainbowGenericProvider) Init(c provider.Oauth2Option) {
p.conf = c
}
func (p *rainbowGenericProvider) Provider() provider.OAuth2Provider {
switch p.t {
case "wx":
return "wechat"
default:
return p.t
}
}
func (p *rainbowGenericProvider) NewAuthURL(ctx context.Context, state string) (string, error) {
result, err := url.JoinPath(p.parent.api, "/connect.php")
if err != nil {
return "", err
}
u, err := url.Parse(result)
if err != nil {
return "", err
}
query := url.Values{}
query.Set("act", "login")
query.Set("appid", p.conf.ClientID)
query.Set("appkey", p.conf.ClientSecret)
query.Set("type", p.t)
query.Set("redirect_uri", p.conf.RedirectURL)
query.Set("state", state)
u.RawQuery = query.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return "", err
}
resp, err := uhc.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data := rainbowNewAuthURLResp{}
err = json.NewDecoder(resp.Body).Decode(&data)
if err != nil {
return "", err
}
if data.Code != 0 {
return "", fmt.Errorf("error code: %d, msg: %s", data.ErrCode, data.Msg)
}
return data.URL, nil
}
type rainbowNewAuthURLResp struct {
Msg string `json:"msg"`
Type string `json:"type"`
URL string `json:"url"`
Code int `json:"code"`
ErrCode int `json:"errcode"`
}
func (p *rainbowGenericProvider) GetUserInfo(
ctx context.Context,
code string,
) (*provider.UserInfo, error) {
result, err := url.JoinPath(p.parent.api, "/connect.php")
if err != nil {
return nil, err
}
u, err := url.Parse(result)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("act", "callback")
query.Set("appid", p.conf.ClientID)
query.Set("appkey", p.conf.ClientSecret)
query.Set("type", p.t)
query.Set("code", code)
u.RawQuery = query.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
}
resp, err := uhc.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data := rainbowUserInfo{}
err = json.NewDecoder(resp.Body).Decode(&data)
if err != nil {
return nil, err
}
if data.Code != 0 {
return nil, fmt.Errorf("error code: %d, msg: %s", data.ErrCode, data.Msg)
}
return &provider.UserInfo{
Username: data.Nickname,
ProviderUserID: data.SocialUID,
}, nil
}
//nolint:tagliatelle
type rainbowUserInfo struct {
Msg string `json:"msg"`
Type string `json:"type"`
SocialUID string `json:"social_uid"`
Nickname string `json:"nickname"`
Code int `json:"code"`
ErrCode int `json:"errcode"`
}
func (r *Rainbow) ExtractProvider(p provider.OAuth2Provider) (provider.Interface, error) {
switch p {
case "qq", "wx", "alipay", "baidu", "microsoft":
return r.newGenericProvider(p), nil
default:
return nil, fmt.Errorf("provider %s not supported", p)
}
}
func init() {
addAggregation(new(Rainbow))
}