Skip to content

Commit

Permalink
init pkg/connector (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
rnons authored Nov 29, 2024
1 parent c55390b commit 74a4761
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 18 deletions.
4 changes: 3 additions & 1 deletion cmd/mautrix-googlechat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package main

import (
"go.mau.fi/mautrix-googlechat/pkg/connector"

"maunium.net/go/mautrix/bridgev2/matrix/mxmain"
)

Expand All @@ -31,7 +33,7 @@ var m = mxmain.BridgeMain{
Description: "A Matrix-Google Chat puppeting bridge",
URL: "https://github.com/mautrix/googlechat",
Version: "0.6.0",
//Connector: c,
Connector: &connector.GChatConnector{},
}

func main() {
Expand Down
93 changes: 93 additions & 0 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package connector

import (
"context"

"go.mau.fi/mautrix-googlechat/pkg/gchatmeow"

"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/bridgev2/simplevent"
)

type GChatClient struct {
UserLogin *bridgev2.UserLogin
Client *gchatmeow.Client
}

var (
_ bridgev2.NetworkAPI = (*GChatClient)(nil)
)

func (c *GChatClient) Connect(ctx context.Context) error {
_, err := c.Client.LoadMessagesPage()
if err != nil {
return err
}
err = c.Client.Connect()
if err != nil {
return err
}
return c.onConnect(ctx)
}

func (c *GChatClient) Disconnect() {
}

func (c *GChatClient) GetCapabilities(ctx context.Context, portal *bridgev2.Portal) *bridgev2.NetworkRoomCapabilities {
return nil
}

func (c *GChatClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) {
return nil, nil
}

func (c *GChatClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) {
return nil, nil
}

func (c *GChatClient) IsLoggedIn() bool {
return false
}

func (c *GChatClient) IsThisUser(ctx context.Context, userID networkid.UserID) bool {
return networkid.UserID(c.UserLogin.ID) == userID
}

func (c *GChatClient) LogoutRemote(ctx context.Context) {
}

func (c *GChatClient) onConnect(ctx context.Context) error {
res, err := c.Client.GetPaginatedWorlds(nil)
if err != nil {
return err
}
for _, item := range res.WorldItems {
// TODO room name for DM, and full members list
name := item.GetRoomName()
memberMap := map[networkid.UserID]bridgev2.ChatMember{}
memberMap[networkid.UserID(c.UserLogin.ID)] = bridgev2.ChatMember{
EventSender: bridgev2.EventSender{
IsFromMe: true,
Sender: networkid.UserID(c.UserLogin.ID),
},
}
c.UserLogin.Bridge.QueueRemoteEvent(c.UserLogin, &simplevent.ChatResync{
EventMeta: simplevent.EventMeta{
Type: bridgev2.RemoteEventChatResync,
PortalKey: networkid.PortalKey{
ID: networkid.PortalID(item.GroupId.String()),
Receiver: c.UserLogin.ID,
},
CreatePortal: true,
},
ChatInfo: &bridgev2.ChatInfo{
Name: &name,
Members: &bridgev2.ChatMemberList{
MemberMap: memberMap,
},
},
})
}
return nil
}
55 changes: 55 additions & 0 deletions pkg/connector/connector.go
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
package connector

import (
"context"

"go.mau.fi/util/configupgrade"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
)

type GChatConnector struct {
Bridge *bridgev2.Bridge
}

var (
_ bridgev2.NetworkConnector = (*GChatConnector)(nil)
)

func (gc *GChatConnector) Init(bridge *bridgev2.Bridge) {
gc.Bridge = bridge
}

func (gc *GChatConnector) Start(ctx context.Context) error {
return nil
}

func (gc *GChatConnector) GetName() bridgev2.BridgeName {
return bridgev2.BridgeName{
DisplayName: "Google Chat",
NetworkURL: "https://chat.google.com",
NetworkIcon: "mxc://maunium.net/BDIWAQcbpPGASPUUBuEGWXnQ",
NetworkID: "googlechat",
BeeperBridgeType: "googlechat",
DefaultPort: 29320,
}
}

func (gc *GChatConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities {
return &bridgev2.NetworkGeneralCapabilities{}
}

func (gc *GChatConnector) GetConfig() (example string, data any, upgrader configupgrade.Upgrader) {
return "", nil, nil
}

func (gc *GChatConnector) GetDBMetaTypes() database.MetaTypes {
return database.MetaTypes{
Portal: nil,
Ghost: nil,
Message: nil,
Reaction: nil,
UserLogin: func() any {
return &UserLoginMetadata{}
},
}
}
11 changes: 11 additions & 0 deletions pkg/connector/handlematrix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package connector

import (
"context"

"maunium.net/go/mautrix/bridgev2"
)

func (c *GChatClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage) (message *bridgev2.MatrixMessageResponse, err error) {
return nil, nil
}
122 changes: 122 additions & 0 deletions pkg/connector/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package connector

import (
"context"
"fmt"

"go.mau.fi/mautrix-googlechat/pkg/gchatmeow"
"go.mau.fi/mautrix-googlechat/pkg/gchatmeow/cookies"
"go.mau.fi/mautrix-googlechat/pkg/gchatmeow/debug"

"maunium.net/go/mautrix/bridge/status"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/id"
)

const (
LoginStepIDCookies = "com.beeper.googlechat.login.cookies"
LoginStepIDComplete = "com.beeper.googlechat.login.complete"
)

func (gc *GChatConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) {
return &GChatCookieLogin{User: user}, nil
}

func (gc *GChatConnector) GetLoginFlows() []bridgev2.LoginFlow {
return []bridgev2.LoginFlow{{
Name: "Cookies",
Description: "Log in with your cookies",
ID: "cookies",
}}
}

func (gc *GChatConnector) LoadUserLogin(ctx context.Context, login *bridgev2.UserLogin) error {
loginMetadata := login.Metadata.(*UserLoginMetadata)
var client *gchatmeow.Client
clientOptions := gchatmeow.ClientOpts{
Cookies: cookies.NewCookiesFromString(loginMetadata.Cookies),
}
client = gchatmeow.NewClient(&clientOptions, debug.NewLogger())
c := &GChatClient{
UserLogin: login,
Client: client,
}
login.Client = c
return nil
}

type GChatCookieLogin struct {
User *bridgev2.User
}

type UserLoginMetadata struct {
Cookies string
}

var _ bridgev2.LoginProcessCookies = (*GChatCookieLogin)(nil)

func (gl *GChatCookieLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
step := &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeCookies,
StepID: LoginStepIDCookies,
Instructions: "Enter a JSON object with your cookies, or a cURL command copied from browser devtools.",
CookiesParams: &bridgev2.LoginCookiesParams{
URL: "https://chat.google.com/",
},
}
return step, nil
}

func (gl *GChatCookieLogin) Cancel() {}

func (gl *GChatCookieLogin) SubmitCookies(ctx context.Context, strCookies map[string]string) (*bridgev2.LoginStep, error) {
cookies := &cookies.Cookies{}
cookies.UpdateValues(strCookies)

clientOptions := gchatmeow.ClientOpts{
Cookies: cookies,
}
client := gchatmeow.NewClient(&clientOptions, debug.NewLogger())

initialData, err := client.LoadMessagesPage()
if err != nil {
return nil, err
}

user := initialData.CurrentUser.Data

userId := user.Id.Id
ul, err := gl.User.NewLogin(ctx, &database.UserLogin{
ID: networkid.UserLoginID(userId),
RemoteName: user.Fullname,
RemoteProfile: status.RemoteProfile{
Name: user.Fullname,
Email: user.Email,
Avatar: id.ContentURIString(user.GetAvatarUrl()),
},
Metadata: &UserLoginMetadata{
Cookies: cookies.String(),
},
}, nil)

if err != nil {
return nil, fmt.Errorf("failed to save new login: %w", err)
}

ul.Client.Connect(ctx)
c := ul.Client.(*GChatClient)
c.UserLogin = ul
c.Client = client

return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeComplete,
StepID: LoginStepIDComplete,
Instructions: fmt.Sprintf("Logged in as %s (%s)", user.Fullname, userId),
CompleteParams: &bridgev2.LoginCompleteParams{
UserLoginID: ul.ID,
UserLogin: ul,
},
}, nil
}
3 changes: 1 addition & 2 deletions pkg/gchatmeow/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (c *Client) LoadMessagesPage() (*types.InitialConfigData, error) {
})

url := fmt.Sprintf("%s?%s", endpoints.MOLE_BASE_URL, "wfi=gtn-brain-iframe-id&hs=%5B%22h_hs%22%2Cnull%2Cnull%2C%5B1%2C0%5D%2Cnull%2Cnull%2C%22gmail.pinto-server_20240610.06_p0%22%2C1%2Cnull%2C%5B15%2C48%2C6%2C43%2C36%2C35%2C26%2C44%2C39%2C46%2C41%2C18%2C24%2C11%2C21%2C14%2C1%2C51%2C53%5D%2Cnull%2Cnull%2C%22GgsaQvppfqU.en..es5%22%2Cnull%2Cnull%2Cnull%2C%5B2%5D%2Cnull%2C0%5D&hl=en&lts=chat%2Fhome&shell=9&has_stream_view=false&origin=https%3A%2F%2Fmail.google.com")
resp, respBody, err := c.MakeRequest(url, "GET", headers, nil, types.NONE)
resp, respBody, err := c.MakeRequest(url, http.MethodGet, headers, nil, types.NONE)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -199,7 +199,6 @@ func (c *Client) SetProxy(proxyAddr string) error {
if err != nil {
return err
}
c.http.Transport.(*http.Transport).Dial = c.socksProxy.Dial
contextDialer, ok := c.socksProxy.(proxy.ContextDialer)
if ok {
c.http.Transport.(*http.Transport).DialContext = contextDialer.DialContext
Expand Down
2 changes: 2 additions & 0 deletions pkg/gchatmeow/client_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//lint:file-ignore U1000 ignore

package gchatmeow_test

import (
Expand Down
9 changes: 9 additions & 0 deletions pkg/gchatmeow/cookies/cookies.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ func NewCookies() *Cookies {
}
}

func (c *Cookies) UpdateValues(newValues map[string]string) {
c.lock.Lock()
defer c.lock.Unlock()
c.store = make(map[GChatCookieName]string)
for k, v := range newValues {
c.store[GChatCookieName(k)] = v
}
}

func NewCookiesFromString(cookieStr string) *Cookies {
c := NewCookies()
cookieStrings := strings.Split(cookieStr, ";")
Expand Down
2 changes: 1 addition & 1 deletion pkg/gchatmeow/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *Client) makeAPIRequest(endpoint endpoints.APIEndpoint, payload proto.Me
return err
}

_, respBody, err := c.MakeRequest(url, "POST", headers, payloadBytes, types.PROTOBUF)
_, respBody, err := c.MakeRequest(url, http.MethodPost, headers, payloadBytes, types.PROTOBUF)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/gchatmeow/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (c *Client) UploadMedia(fileName string, mediaContentType string, mediaByte
Extra: extraHeaders,
})
url := fmt.Sprintf("%s?%s", endpoints.UPLOADS, queryString)
resp, _, err := c.MakeRequest(url, "POST", headers, nil, types.FORM_PLAINTEXT_UTF8)
resp, _, err := c.MakeRequest(url, http.MethodPost, headers, nil, types.FORM_PLAINTEXT_UTF8)
if err != nil {
return nil, err
}
Expand All @@ -57,7 +57,7 @@ func (c *Client) finalizeMediaUpload(uploadUrl string, mediaBytes []byte, header
headers.Set("X-Goog-Upload-Entity-MD5", md5Entity)
headers.Set("X-Goog-Upload-Offset", "0")

_, respBody, err := c.MakeRequest(uploadUrl, "PUT", headers, mediaBytes, types.FORM_PLAINTEXT_UTF8)
_, respBody, err := c.MakeRequest(uploadUrl, http.MethodPut, headers, mediaBytes, types.FORM_PLAINTEXT_UTF8)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/gchatmeow/realtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (rc *RealtimeClient) Connect() error {
WithCookies: true,
WithXClientData: true,
})
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -252,7 +252,7 @@ func (rc *RealtimeClient) sendPing() error {
}

url := endpoints.WEBCHANNEL_EVENTS + "?" + queryArgs
req, respBody, err := rc.client.MakeRequest(url, "POST", headers, []byte(encodedPayload), types.FORM)
req, respBody, err := rc.client.MakeRequest(url, http.MethodPost, headers, []byte(encodedPayload), types.FORM)
if err != nil {
return err
}
Expand All @@ -276,7 +276,7 @@ func (rc *RealtimeClient) Register() error {
},
})

resp, respBody, err := rc.client.MakeRequest(url, "GET", headers, nil, types.NONE)
resp, respBody, err := rc.client.MakeRequest(url, http.MethodGet, headers, nil, types.NONE)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 74a4761

Please sign in to comment.