wechaty-puppet-service/puppet_service.go
package puppetservice
import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
pbwechaty "github.com/wechaty/go-grpc/wechaty"
pbwechatypuppet "github.com/wechaty/go-grpc/wechaty/puppet"
wechatyPuppet "github.com/wechaty/go-wechaty/wechaty-puppet"
"github.com/wechaty/go-wechaty/wechaty-puppet/filebox"
"github.com/wechaty/go-wechaty/wechaty-puppet/helper"
"github.com/wechaty/go-wechaty/wechaty-puppet/schemas"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"io"
"strings"
"time"
)
var (
// ErrNoEndpoint err no endpoint
ErrNoEndpoint = errors.New("no endpoint")
// ErrURLLinkPayloadNotFound ...
ErrURLLinkPayloadNotFound = errors.New("UrlLinkPayloadNotFound")
)
var pbEventType2PuppetEventName = schemas.PbEventType2PuppetEventName()
var pbEventType2GeneratePayloadFunc = schemas.PbEventType2GeneratePayloadFunc()
var _ wechatyPuppet.IPuppetAbstract = &PuppetService{}
// PuppetService struct
type PuppetService struct {
*wechatyPuppet.Puppet
grpcConn *grpc.ClientConn
grpcClient pbwechaty.PuppetClient
eventStream pbwechaty.Puppet_EventClient
opts Options
disableTLS bool
serverName string
endpoint string
token string
caCert string
started chan struct{}
stop chan struct{}
}
// NewPuppetService new PuppetService struct
// Deprecated: please use NewNewPuppetService
func NewPuppetService(o wechatyPuppet.Option) (*PuppetService, error) {
return NewNewPuppetService(Options{
Option: o,
GrpcReconnectInterval: o.GrpcReconnectInterval, //nolint:staticcheck
})
}
// NewNewPuppetService create puppet service
func NewNewPuppetService(opts Options) (*PuppetService, error) {
token, err := envServiceToken(opts.Token)
if err != nil {
return nil, err
}
endpoint := envEndpoint(opts.Endpoint)
if endpoint == "" {
endpoint = fmt.Sprintf("wechaty://%s/%s",
envAuthority(opts.Authority), token)
}
//disableTLS := envNoTLSInsecureClient(opts.TLS.Disable)
// TODO 禁用 tls, type script ca 在 golang 不能使用
disableTLS := true
serverNameIndication := envTLSServerName(opts.TLS.ServerName)
if serverNameIndication == "" {
serverNameIndication = sni(token)
}
if serverNameIndication == "" {
return nil, fmt.Errorf(
`wechaty Puppet Service requires a SNI as prefix of the token.
You can add the "%s_" prefix to your token
like: "%s_%s
and try again`, TLSInsecureServerCertCommonName, TLSInsecureServerCertCommonName, token)
}
// TODO puppet is poorly designed, consider refactoring
puppetAbstract, err := wechatyPuppet.NewPuppet(opts.Option)
if err != nil {
return nil, err
}
puppetService := &PuppetService{
Puppet: puppetAbstract,
disableTLS: disableTLS,
serverName: serverNameIndication,
endpoint: endpoint,
token: token,
caCert: envTLSCaCert(opts.TLS.CaCert),
opts: opts,
stop: make(chan struct{}, 1),
started: make(chan struct{}, 1),
}
puppetAbstract.SetPuppetImplementation(puppetService)
return puppetService, nil
}
func sni(token string) string {
underscoreIndex := strings.LastIndex(token, "_")
if underscoreIndex == -1 {
return ""
}
return strings.ToLower(token[0:underscoreIndex])
}
// MessageImage ...
func (p *PuppetService) MessageImage(messageID string, imageType schemas.ImageType) (*filebox.FileBox, error) {
log.Tracef("PuppetService MessageImage(%s, %s)\n", messageID, imageType)
response, err := p.grpcClient.MessageImage(context.Background(), &pbwechatypuppet.MessageImageRequest{
Id: messageID,
Type: pbwechatypuppet.ImageType(imageType),
})
if err != nil {
return nil, err
}
return filebox.FromJSON(response.FileBox), nil
}
// Start ...
func (p *PuppetService) Start() (err error) {
log.Tracef("PuppetService Start()")
defer func() {
if err != nil {
err = fmt.Errorf("PuppetService Start() rejection: %w", err)
}
}()
err = p.startGrpcClient()
if err != nil {
return err
}
filebox.SetUuidLoader(p.uuidLoader)
filebox.SetUuidSaver(p.uuidSaver)
err = p.startGrpcStream()
if err != nil {
return err
}
_, err = p.grpcClient.Start(context.Background(), &pbwechatypuppet.StartRequest{})
if err != nil {
return err
}
p.started <- struct{}{}
return nil
}
func (p *PuppetService) uuidSaver(reader io.Reader) (uuid string, err error) {
client, err := p.grpcClient.Upload(context.Background())
if err != nil {
return "", err
}
b := make([]byte, 4000000)
for {
l, err := reader.Read(b)
if err == io.EOF {
break
}
if err != nil {
return "", err
}
err = client.Send(&pbwechatypuppet.UploadRequest{Chunk: b[0:l]})
if err != nil {
return "", err
}
}
response, err := client.CloseAndRecv()
if err != nil {
return "", err
}
return response.Id, nil
}
func (p *PuppetService) uuidLoader(uuid string) (io.Reader, error) {
client, err := p.grpcClient.Download(context.Background(), &pbwechatypuppet.DownloadRequest{
Id: uuid,
})
if err != nil {
return nil, err
}
return NewDownloadFile(client), nil
}
// Stop ...
func (p *PuppetService) Stop() {
p.stop <- struct{}{}
var err error
defer func() {
if err != nil {
log.Errorf("PuppetService Stop err: %s\n", err)
}
}()
if p.logonoff() {
p.Emit(schemas.EventLogoutPayload{
ContactId: p.SelfID(),
Data: "PuppetService Stop()",
})
p.SetID("")
}
if err = p.stopGrpcStream(); err != nil {
return
}
if p.grpcClient != nil {
if _, err = p.grpcClient.Stop(context.Background(), &pbwechatypuppet.StopRequest{}); err != nil {
return
}
}
if err = p.stopGrpcClient(); err != nil {
return
}
}
func (p *PuppetService) stopGrpcClient() error {
if p.grpcConn == nil {
return errors.New("puppetClient had not inited")
}
p.grpcConn.Close()
p.grpcConn = nil
p.grpcClient = nil
return nil
}
func (p *PuppetService) stopGrpcStream() error {
log.Trace("PuppetService stopGrpcStream()")
if p.eventStream == nil {
return errors.New("no event stream")
}
if err := p.eventStream.CloseSend(); err != nil {
log.Tracef("PuppetService stopGrpcStream() err: %s\n", err)
}
p.eventStream = nil
return nil
}
func (p *PuppetService) logonoff() bool {
return p.SelfID() != ""
}
func (p *PuppetService) createCred() (credentials.TransportCredentials, error) {
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM([]byte(p.caCert)); !ok {
return nil, fmt.Errorf("PuppetService.createCred failed to parse root certificate")
}
return credentials.NewClientTLSFromCert(pool, p.serverName), nil
}
func (p *PuppetService) startGrpcStream() (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("startGrpcStream err:%w", err)
}
}()
if p.eventStream != nil {
return errors.New("event stream exists")
}
p.eventStream, err = p.grpcClient.Event(context.Background(), &pbwechatypuppet.EventRequest{})
if err != nil {
return err
}
go func() {
for {
reply, err := p.eventStream.Recv()
if err == io.EOF {
log.Error("eventStream.Recv EOF")
break
}
if err != nil {
log.Errorf("PuppetService startGrpcStream() eventStream err %s", err)
reason := "startGrpcStream() eventStream err: " + err.Error()
p.Emit(schemas.PuppetEventNameReset, schemas.EventResetPayload{Data: reason})
p.eventStream = nil
break
}
go p.onGrpcStreamEvent(reply)
}
}()
return nil
}
func (p *PuppetService) onGrpcStreamEvent(event *pbwechatypuppet.EventResponse) {
log.Tracef("PuppetService onGrpcStreamEvent({type:%s payload:%s})", event.Type, event.Payload)
if event.Type != pbwechatypuppet.EventType_EVENT_TYPE_HEARTBEAT {
p.Emit(schemas.PuppetEventNameHeartbeat, &schemas.EventHeartbeatPayload{
Data: fmt.Sprintf("onGrpcStreamEvent(%s)", event.Type),
})
}
if event.Type == pbwechatypuppet.EventType_EVENT_TYPE_UNSPECIFIED {
log.Warn("PuppetService onGrpcStreamEvent() got an EventType.EVENT_TYPE_UNSPECIFIED ")
return
}
eventName, ok := pbEventType2PuppetEventName[event.Type]
if !ok {
log.Warnf("'eventType %s unsupported! (code should not reach here)\n", event.Type)
return
}
payload := pbEventType2GeneratePayloadFunc[event.Type]()
p.unMarshal(event.Payload, payload)
switch event.Type {
case pbwechatypuppet.EventType_EVENT_TYPE_RESET:
log.Warnf("PuppetService onGrpcStreamEvent() got an EventType.EVENT_TYPE_RESET ?")
// the `reset` event should be dealed not send out
return
case pbwechatypuppet.EventType_EVENT_TYPE_LOGIN:
p.SetID(payload.(*schemas.EventLoginPayload).ContactId)
case pbwechatypuppet.EventType_EVENT_TYPE_LOGOUT:
p.SetID("")
}
p.Emit(eventName, payload)
}
func (p *PuppetService) unMarshal(data string, v interface{}) {
err := json.Unmarshal([]byte(data), v)
if err != nil {
log.Errorf("PuppetService unMarshal err: %s\n", err)
}
}
// Logout ...
func (p *PuppetService) Logout() error {
log.Tracef("PuppetService Logout()")
if !p.logonoff() {
return errors.New("logout before login? ")
}
_, err := p.grpcClient.Logout(context.Background(), &pbwechatypuppet.LogoutRequest{})
if err != nil {
return fmt.Errorf("PuppetService Logout() err: %w", err)
}
go p.Emit(schemas.PuppetEventNameLogout, &schemas.EventLogoutPayload{
ContactId: p.SelfID(),
})
p.SetID("")
return nil
}
// Ding ...
func (p *PuppetService) Ding(data string) {
log.Tracef("PuppetService Ding(%s)\n", data)
_, err := p.grpcClient.Ding(context.Background(), &pbwechatypuppet.DingRequest{
Data: data,
})
if err != nil {
log.Tracef("PuppetService Ding() err: %s\n", err)
}
}
// SetContactAlias ...
func (p *PuppetService) SetContactAlias(contactID string, alias string) error {
log.Tracef("PuppetService, SetContactAlias(%s, %s)\n", contactID, alias)
_, err := p.grpcClient.ContactAlias(context.Background(), &pbwechatypuppet.ContactAliasRequest{
Id: contactID,
Alias: &alias,
})
if err != nil {
return fmt.Errorf("PuppetService SetContactAlias err: %w", err)
}
return nil
}
// ContactAlias ...
func (p *PuppetService) ContactAlias(contactID string) (string, error) {
log.Tracef("PuppetService, 'ContactAlias(%s)\n", contactID)
response, err := p.grpcClient.ContactAlias(context.Background(), &pbwechatypuppet.ContactAliasRequest{
Id: contactID,
})
if err != nil {
return "", fmt.Errorf("PuppetService ContactAlias err: %w", err)
}
return response.Alias, nil
}
// ContactList ...
func (p *PuppetService) ContactList() ([]string, error) {
log.Trace("PuppetService ContactList()")
response, err := p.grpcClient.ContactList(context.Background(), &pbwechatypuppet.ContactListRequest{})
if err != nil {
return nil, fmt.Errorf("PuppetService ContactList err: %w", err)
}
return response.Ids, nil
}
// ContactQRCode ...
func (p *PuppetService) ContactQRCode(contactID string) (string, error) {
log.Tracef("PuppetService ContactQRCode(%s)\n", contactID)
if contactID != p.SelfID() {
return "", errors.New("can not set avatar for others")
}
response, err := p.grpcClient.ContactSelfQRCode(context.Background(), &pbwechatypuppet.ContactSelfQRCodeRequest{})
if err != nil {
return "", err
}
return response.Qrcode, nil
}
// SetContactAvatar ...
func (p *PuppetService) SetContactAvatar(contactID string, fileBox *filebox.FileBox) error {
log.Tracef("PuppetService SetContactAvatar(%s)\n", contactID)
var err error
fileBox, err = serializeFileBox(fileBox)
if err != nil {
return fmt.Errorf("serializeFileBox %w", err)
}
jsonString, err := fileBox.ToJSON()
if err != nil {
return fmt.Errorf("fileBox.ToJSON() %w", err)
}
_, err = p.grpcClient.ContactAvatar(context.Background(), &pbwechatypuppet.ContactAvatarRequest{
Id: contactID,
FileBox: &jsonString,
})
if err != nil {
return err
}
return nil
}
// ContactAvatar ...
func (p *PuppetService) ContactAvatar(contactID string) (*filebox.FileBox, error) {
log.Tracef("PuppetService ContactAvatar(%s)\n", contactID)
response, err := p.grpcClient.ContactAvatar(context.Background(), &pbwechatypuppet.ContactAvatarRequest{
Id: contactID,
})
if err != nil {
return nil, err
}
return filebox.FromJSON(response.FileBox), nil
}
// ContactRawPayload ...
func (p *PuppetService) ContactRawPayload(contactID string) (*schemas.ContactPayload, error) {
log.Tracef("PuppetService ContactRawPayload(%s)\n", contactID)
response, err := p.grpcClient.ContactPayload(context.Background(), &pbwechatypuppet.ContactPayloadRequest{
Id: contactID,
})
if err != nil {
return nil, err
}
return &schemas.ContactPayload{
Id: response.Id,
Gender: schemas.ContactGender(response.Gender),
Type: schemas.ContactType(response.Type),
Name: response.Name,
Avatar: response.Avatar,
Address: response.Address,
Alias: response.Alias,
City: response.City,
Friend: response.Friend,
Province: response.Province,
Signature: response.Signature,
Star: response.Star,
WeiXin: response.Weixin,
}, nil
}
// SetContactSelfName ...
func (p *PuppetService) SetContactSelfName(name string) error {
log.Tracef("PuppetService SetContactSelfName(%s)\n", name)
_, err := p.grpcClient.ContactSelfName(context.Background(), &pbwechatypuppet.ContactSelfNameRequest{
Name: name,
})
return err
}
// ContactSelfQRCode ...
func (p *PuppetService) ContactSelfQRCode() (string, error) {
log.Tracef("PuppetService ContactSelfQRCode()")
response, err := p.grpcClient.ContactSelfQRCode(context.Background(), &pbwechatypuppet.ContactSelfQRCodeRequest{})
if err != nil {
return "", err
}
return response.Qrcode, nil
}
// SetContactSelfSignature ...
func (p *PuppetService) SetContactSelfSignature(signature string) error {
log.Tracef("PuppetService SetContactSelfSignature(%s)\n", signature)
_, err := p.grpcClient.ContactSelfSignature(context.Background(), &pbwechatypuppet.ContactSelfSignatureRequest{
Signature: signature,
})
return err
}
// MessageRawMiniProgramPayload ...
func (p *PuppetService) MessageRawMiniProgramPayload(messageID string) (*schemas.MiniProgramPayload, error) {
log.Tracef("PuppetService MessageMiniProgram(%s)\n", messageID)
response, err := p.grpcClient.MessageMiniProgram(context.Background(), &pbwechatypuppet.MessageMiniProgramRequest{
Id: messageID,
})
if err != nil {
return nil, err
}
// Deprecated: will be removed after Dec 31, 2022
//nolint:staticcheck
if response.MiniProgram == nil {
payload := &schemas.MiniProgramPayload{}
p.unMarshal(response.MiniProgramDeprecated, payload)
return payload, nil
}
payload := &schemas.MiniProgramPayload{
Appid: response.MiniProgram.Appid,
Description: response.MiniProgram.Description,
PagePath: response.MiniProgram.PagePath,
ThumbUrl: response.MiniProgram.ThumbUrl,
Title: response.MiniProgram.Title,
Username: response.MiniProgram.Username,
ThumbKey: response.MiniProgram.ThumbKey,
ShareId: response.MiniProgram.ShareId,
IconUrl: response.MiniProgram.IconUrl,
}
return payload, nil
}
// MessageContact ...
func (p *PuppetService) MessageContact(messageID string) (string, error) {
log.Tracef("PuppetService MessageContact(%s)\n", messageID)
response, err := p.grpcClient.MessageContact(context.Background(), &pbwechatypuppet.MessageContactRequest{
Id: messageID,
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageSendMiniProgram ...
func (p *PuppetService) MessageSendMiniProgram(conversationID string, miniProgramPayload *schemas.MiniProgramPayload) (string, error) {
log.Tracef("PuppetService MessageSendMiniProgram(%s,%#v)\n", conversationID, miniProgramPayload)
response, err := p.grpcClient.MessageSendMiniProgram(context.Background(), &pbwechatypuppet.MessageSendMiniProgramRequest{
ConversationId: conversationID,
MiniProgram: &pbwechatypuppet.MiniProgramPayload{
Appid: miniProgramPayload.Appid,
Description: miniProgramPayload.Description,
PagePath: miniProgramPayload.PagePath,
IconUrl: miniProgramPayload.IconUrl,
ShareId: miniProgramPayload.ShareId,
ThumbUrl: miniProgramPayload.ThumbUrl,
Title: miniProgramPayload.Title,
Username: miniProgramPayload.Username,
ThumbKey: miniProgramPayload.ThumbKey,
},
// Deprecated: will be removed after Dec 31, 2022
MiniProgramDeprecated: miniProgramPayload.ToJson(),
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageRecall ...
func (p *PuppetService) MessageRecall(messageID string) (bool, error) {
log.Tracef("PuppetService MessageRecall(%s)\n", messageID)
response, err := p.grpcClient.MessageRecall(context.Background(), &pbwechatypuppet.MessageRecallRequest{
Id: messageID,
})
if err != nil {
return false, err
}
return response.Success, nil
}
// MessageFile ...
func (p *PuppetService) MessageFile(id string) (*filebox.FileBox, error) {
log.Tracef("PuppetService MessageFile(%s)\n", id)
response, err := p.grpcClient.MessageFileStream(context.Background(), &pbwechatypuppet.MessageFileStreamRequest{
Id: id,
})
if err != nil {
return nil, err
}
return NewFileBoxFromMessageFileStream(response)
}
// MessageLocation get location payload
func (p *PuppetService) MessageLocation(messageID string) (*schemas.LocationPayload, error) {
log.Tracef("PuppetService MessageLocation(%s)\n", messageID)
response, err := p.grpcClient.MessageLocation(context.Background(), &pbwechatypuppet.MessageLocationRequest{
Id: messageID,
})
if err != nil {
return nil, err
}
if response.Location == nil {
return &schemas.LocationPayload{
Address: "NOADDRESS",
Name: "NONAME",
}, nil
}
return &schemas.LocationPayload{
Accuracy: response.Location.Accuracy,
Address: response.Location.Address,
Latitude: response.Location.Latitude,
Longitude: response.Location.Longitude,
Name: response.Location.Name,
}, nil
}
// MessageSendLocation send location
func (p *PuppetService) MessageSendLocation(conversationID string, payload *schemas.LocationPayload) (string, error) {
log.Tracef("PuppetService MessageSendLocation(%s, %+v)\n", conversationID, payload)
response, err := p.grpcClient.MessageSendLocation(context.Background(), &pbwechatypuppet.MessageSendLocationRequest{
ConversationId: conversationID,
Location: &pbwechatypuppet.LocationPayload{
Accuracy: payload.Accuracy,
Address: payload.Address,
Latitude: payload.Latitude,
Longitude: payload.Longitude,
Name: payload.Name,
},
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageRawPayload ...
func (p *PuppetService) MessageRawPayload(id string) (*schemas.MessagePayload, error) {
log.Tracef("PuppetService MessagePayload(%s)\n", id)
response, err := p.grpcClient.MessagePayload(context.Background(), &pbwechatypuppet.MessagePayloadRequest{
Id: id,
})
if err != nil {
return nil, err
}
payload := &schemas.MessagePayload{
MessagePayloadBase: schemas.MessagePayloadBase{
Id: response.Id,
MentionIdList: response.MentionIds,
FileName: response.Filename,
Text: response.Text,
Type: schemas.MessageType(response.Type),
},
MessagePayloadRoom: schemas.MessagePayloadRoom{
TalkerId: response.TalkerId,
RoomId: response.RoomId,
ListenerId: response.ListenerId,
},
}
if response.ReceiveTime != nil {
payload.Timestamp = grpcTimestampToGoTime(response.ReceiveTime)
} else {
payload.Timestamp = time.Unix(int64(response.TimestampDeprecated), 0) //nolint:staticcheck
}
return payload, nil
}
// MessageSendText ...
func (p *PuppetService) MessageSendText(conversationID string, text string, mentionIDList ...string) (string, error) {
log.Tracef("PuppetService messageSendText(%s, %s)\n", conversationID, text)
response, err := p.grpcClient.MessageSendText(context.Background(), &pbwechatypuppet.MessageSendTextRequest{
ConversationId: conversationID,
Text: text,
MentionalIds: mentionIDList,
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageSendFile ...
func (p *PuppetService) MessageSendFile(conversationID string, fileBox *filebox.FileBox) (string, error) {
log.Tracef("PuppetService MessageSendFile(%s)\n", conversationID)
if msgID, err := p.messageSendFileNonStream(conversationID, fileBox); err == nil {
return msgID, nil
}
return p.messageSendFileStream(conversationID, fileBox)
}
func (p *PuppetService) messageSendFileStream(conversationID string, fileBox *filebox.FileBox) (string, error) {
stream, err := p.grpcClient.MessageSendFileStream(context.Background())
if err != nil {
return "", err
}
writer, err := ToMessageSendFileWriter(stream, conversationID, fileBox)
if err != nil {
return "", err
}
reader, err := fileBox.ToReader()
if err != nil {
return "", err
}
b := make([]byte, 4000000)
for {
l, err := reader.Read(b)
if err == io.EOF {
break
}
if err != nil {
return "", err
}
_, err = writer.Write(b[0:l])
if err == io.EOF {
break
}
if err != nil {
return "", err
}
}
response, err := stream.CloseAndRecv()
if err != nil {
return "", err
}
return response.Id, nil
}
var serializableFileBoxTypes = helper.ArrayInt{
filebox.TypeBase64,
filebox.TypeUrl,
filebox.TypeQRCode,
}
func (p *PuppetService) messageSendFileNonStream(conversationID string, fileBox *filebox.FileBox) (string, error) {
log.Tracef("PuppetService MessageSendFile(%s)\n", conversationID)
var err error
jsonText := ""
if serializableFileBoxTypes.InArray(int(fileBox.Type())) {
jsonText, err = fileBox.ToJSON()
if err != nil {
return "", err
}
} else {
base64, err := fileBox.ToBase64()
if err != nil {
return "", err
}
jsonText, err = filebox.FromBase64(base64, filebox.WithName(fileBox.Name), filebox.WithMetadata(fileBox.MetaData())).ToJSON()
if err != nil {
return "", err
}
}
response, err := p.grpcClient.MessageSendFile(context.Background(), &pbwechatypuppet.MessageSendFileRequest{
ConversationId: conversationID,
FileBox: jsonText,
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageSendContact ...
func (p *PuppetService) MessageSendContact(conversationID string, contactID string) (string, error) {
log.Tracef("PuppetService MessageSendContact(%s, %s)\n", conversationID, contactID)
response, err := p.grpcClient.MessageSendContact(context.Background(), &pbwechatypuppet.MessageSendContactRequest{
ConversationId: conversationID,
ContactId: contactID,
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageSendURL ...
func (p *PuppetService) MessageSendURL(conversationID string, urlLinkPayload *schemas.UrlLinkPayload) (string, error) {
log.Tracef("PuppetService MessageSendURL(%s, %+v)\n", conversationID, urlLinkPayload)
response, err := p.grpcClient.MessageSendUrl(context.Background(), &pbwechatypuppet.MessageSendUrlRequest{
ConversationId: conversationID,
UrlLink: &pbwechatypuppet.UrlLinkPayload{
Description: urlLinkPayload.Description,
ThumbnailUrl: urlLinkPayload.ThumbnailUrl,
Title: urlLinkPayload.Title,
Url: urlLinkPayload.Url,
},
// Deprecated: will be removed after Dec 31, 2022
UrlLinkDeprecated: urlLinkPayload.ToJson(),
})
if err != nil {
return "", err
}
return response.Id, nil
}
// MessageURL ...
func (p *PuppetService) MessageURL(messageID string) (*schemas.UrlLinkPayload, error) {
log.Tracef("PuppetService MessageURL(%s)\n", messageID)
response, err := p.grpcClient.MessageUrl(context.Background(), &pbwechatypuppet.MessageUrlRequest{
Id: messageID,
})
if err != nil {
return nil, err
}
if response.UrlLink == nil {
// Deprecated: will be removed after Dec 31, 2022
payload := &schemas.UrlLinkPayload{}
p.unMarshal(response.UrlLinkDeprecated, payload) //nolint:staticcheck
return payload, nil
}
payload := &schemas.UrlLinkPayload{
Description: response.UrlLink.Description,
ThumbnailUrl: response.UrlLink.ThumbnailUrl,
Title: response.UrlLink.Title,
Url: response.UrlLink.Url,
}
return payload, nil
}
// RoomRawPayload ...
func (p *PuppetService) RoomRawPayload(id string) (*schemas.RoomPayload, error) {
log.Tracef("PuppetService RoomRawPayload(%s)\n", id)
response, err := p.grpcClient.RoomPayload(context.Background(), &pbwechatypuppet.RoomPayloadRequest{
Id: id,
})
if err != nil {
return nil, err
}
return &schemas.RoomPayload{
Id: response.Id,
Topic: response.Topic,
Avatar: response.Avatar,
MemberIdList: response.MemberIds,
OwnerId: response.OwnerId,
AdminIdList: response.AdminIds,
}, nil
}
// RoomList ...
func (p *PuppetService) RoomList() ([]string, error) {
log.Tracef("PuppetService RoomList()\n")
response, err := p.grpcClient.RoomList(context.Background(), &pbwechatypuppet.RoomListRequest{})
if err != nil {
return nil, err
}
return response.Ids, nil
}
// RoomDel ...
func (p *PuppetService) RoomDel(roomID, contactID string) error {
log.Tracef("PuppetService roomDel(%s, %s)\n", roomID, contactID)
_, err := p.grpcClient.RoomDel(context.Background(), &pbwechatypuppet.RoomDelRequest{
Id: roomID,
ContactId: contactID,
})
if err != nil {
return err
}
return nil
}
// RoomAvatar ...
func (p *PuppetService) RoomAvatar(roomID string) (*filebox.FileBox, error) {
log.Tracef("PuppetService RoomAvatar(%s)\n", roomID)
response, err := p.grpcClient.RoomAvatar(context.Background(), &pbwechatypuppet.RoomAvatarRequest{
Id: roomID,
})
if err != nil {
return nil, err
}
return filebox.FromJSON(response.FileBox), nil
}
// RoomAdd ...
func (p *PuppetService) RoomAdd(roomID, contactID string) error {
log.Tracef("PuppetService RoomAdd(%s, %s)\n", roomID, contactID)
_, err := p.grpcClient.RoomAdd(context.Background(), &pbwechatypuppet.RoomAddRequest{
Id: roomID,
ContactId: contactID,
})
if err != nil {
return err
}
return nil
}
// SetRoomTopic ...
func (p *PuppetService) SetRoomTopic(roomID string, topic string) error {
log.Tracef("PuppetService setRoomTopic(%s, %s)\n", roomID, topic)
_, err := p.grpcClient.RoomTopic(context.Background(), &pbwechatypuppet.RoomTopicRequest{
Id: roomID,
Topic: &topic,
})
return err
}
// RoomTopic ...
func (p *PuppetService) RoomTopic(roomID string) (string, error) {
log.Tracef("PuppetService RoomTopic(%s)\n", roomID)
response, err := p.grpcClient.RoomTopic(context.Background(), &pbwechatypuppet.RoomTopicRequest{
Id: roomID,
})
if err != nil {
return "", err
}
return response.Topic, nil
}
// RoomCreate ...
func (p *PuppetService) RoomCreate(contactIDList []string, topic string) (string, error) {
log.Tracef("PuppetService roomCreate(%s, %s)\n", contactIDList, topic)
response, err := p.grpcClient.RoomCreate(context.Background(), &pbwechatypuppet.RoomCreateRequest{
ContactIds: contactIDList,
Topic: topic,
})
if err != nil {
return "", err
}
return response.Id, nil
}
// RoomQuit ...
func (p *PuppetService) RoomQuit(roomID string) error {
log.Tracef("PuppetService RoomQuit(%s)\n", roomID)
_, err := p.grpcClient.RoomQuit(context.Background(), &pbwechatypuppet.RoomQuitRequest{
Id: roomID,
})
if err != nil {
return err
}
return nil
}
// RoomQRCode ...
func (p *PuppetService) RoomQRCode(roomID string) (string, error) {
log.Tracef("PuppetService RoomQRCode(%s)\n", roomID)
response, err := p.grpcClient.RoomQRCode(context.Background(), &pbwechatypuppet.RoomQRCodeRequest{
Id: roomID,
})
if err != nil {
return "", err
}
return response.Qrcode, nil
}
// RoomMemberList ...
func (p *PuppetService) RoomMemberList(roomID string) ([]string, error) {
log.Tracef("PuppetService RoomMemberList(%s)\n", roomID)
response, err := p.grpcClient.RoomMemberList(context.Background(), &pbwechatypuppet.RoomMemberListRequest{
Id: roomID,
})
if err != nil {
return nil, err
}
return response.MemberIds, nil
}
// RoomMemberRawPayload ...
func (p *PuppetService) RoomMemberRawPayload(roomID string, contactID string) (*schemas.RoomMemberPayload, error) {
log.Tracef("PuppetService RoomMemberRawPayload(%s, %s)\n", roomID, contactID)
response, err := p.grpcClient.RoomMemberPayload(context.Background(), &pbwechatypuppet.RoomMemberPayloadRequest{
Id: roomID,
MemberId: contactID,
})
if err != nil {
return nil, err
}
return &schemas.RoomMemberPayload{
Id: response.Id,
RoomAlias: response.RoomAlias,
InviterId: response.InviterId,
Avatar: response.Avatar,
Name: response.Name,
}, nil
}
// SetRoomAnnounce ...
func (p *PuppetService) SetRoomAnnounce(roomID, text string) error {
log.Tracef("PuppetService SetRoomAnnounce(%s, %s)\n", roomID, text)
_, err := p.grpcClient.RoomAnnounce(context.Background(), &pbwechatypuppet.RoomAnnounceRequest{
Id: roomID,
Text: &text,
})
if err != nil {
return err
}
return nil
}
// RoomAnnounce ...
func (p *PuppetService) RoomAnnounce(roomID string) (string, error) {
log.Tracef("PuppetService RoomAnnounce(%s)\n", roomID)
response, err := p.grpcClient.RoomAnnounce(context.Background(), &pbwechatypuppet.RoomAnnounceRequest{
Id: roomID,
})
if err != nil {
return "", err
}
return response.Text, nil
}
// RoomInvitationAccept ...
func (p *PuppetService) RoomInvitationAccept(roomInvitationID string) error {
log.Tracef("PuppetService RoomInvitationAccept(%s)\n", roomInvitationID)
_, err := p.grpcClient.RoomInvitationAccept(context.Background(), &pbwechatypuppet.RoomInvitationAcceptRequest{
Id: roomInvitationID,
})
return err
}
// RoomInvitationRawPayload ...
func (p *PuppetService) RoomInvitationRawPayload(id string) (*schemas.RoomInvitationPayload, error) {
log.Tracef("PuppetService RoomInvitationRawPayload(%s)\n", id)
response, err := p.grpcClient.RoomInvitationPayload(context.Background(), &pbwechatypuppet.RoomInvitationPayloadRequest{
Id: id,
})
if err != nil {
return nil, err
}
return &schemas.RoomInvitationPayload{
Id: response.Id,
InviterId: response.InviterId,
Topic: response.Topic,
Avatar: response.Avatar,
Invitation: response.Invitation,
MemberCount: int(response.MemberCount),
MemberIdList: response.MemberIds,
Timestamp: grpcTimestampToGoTime(response.ReceiveTime),
ReceiverId: response.ReceiverId,
}, nil
}
// FriendshipSearchPhone ...
func (p *PuppetService) FriendshipSearchPhone(phone string) (string, error) {
log.Tracef("PuppetService FriendshipSearchPhone(%s)\n", phone)
response, err := p.grpcClient.FriendshipSearchPhone(context.Background(), &pbwechatypuppet.FriendshipSearchPhoneRequest{
Phone: phone,
})
if err != nil {
return "", err
}
return response.ContactId, nil
}
// FriendshipSearchWeixin ...
func (p *PuppetService) FriendshipSearchWeixin(weixin string) (string, error) {
log.Tracef("PuppetService FriendshipSearchWeixin(%s)\n", weixin)
response, err := p.grpcClient.FriendshipSearchWeixin(context.Background(), &pbwechatypuppet.FriendshipSearchHandleRequest{
Weixin: weixin,
})
if err != nil {
return "", err
}
return response.ContactId, nil
}
// FriendshipRawPayload ...
func (p *PuppetService) FriendshipRawPayload(id string) (*schemas.FriendshipPayload, error) {
log.Tracef("PuppetService FriendshipRawPayload(%s)\n", id)
response, err := p.grpcClient.FriendshipPayload(context.Background(), &pbwechatypuppet.FriendshipPayloadRequest{
Id: id,
})
if err != nil {
return nil, err
}
return &schemas.FriendshipPayload{
FriendshipPayloadReceive: schemas.FriendshipPayloadReceive{
FriendshipPayloadBase: schemas.FriendshipPayloadBase{
ContactId: response.ContactId,
Id: response.Id,
Hello: response.Hello,
},
Type: schemas.FriendshipType(response.Type),
Scene: schemas.FriendshipSceneType(response.Scene),
Stranger: response.Stranger,
Ticket: response.Ticket,
},
}, nil
}
// FriendshipAdd ...
func (p *PuppetService) FriendshipAdd(contactID, hello string) (err error) {
log.Tracef("PuppetService FriendshipAdd(%s, %s)\n", contactID, hello)
_, err = p.grpcClient.FriendshipAdd(context.Background(), &pbwechatypuppet.FriendshipAddRequest{
ContactId: contactID,
Hello: hello,
})
return err
}
// FriendshipAccept ...
func (p *PuppetService) FriendshipAccept(friendshipID string) (err error) {
log.Tracef("PuppetService FriendshipAccept(%s)\n", friendshipID)
_, err = p.grpcClient.FriendshipAccept(context.Background(), &pbwechatypuppet.FriendshipAcceptRequest{
Id: friendshipID,
})
return err
}
// TagContactAdd ...
func (p *PuppetService) TagContactAdd(id, contactID string) (err error) {
log.Tracef("PuppetService TagContactAdd(%s, %s)\n", id, contactID)
_, err = p.grpcClient.TagContactAdd(context.Background(), &pbwechatypuppet.TagContactAddRequest{
Id: id,
ContactId: id,
})
return err
}
// TagContactRemove ...
func (p *PuppetService) TagContactRemove(id, contactID string) (err error) {
log.Tracef("PuppetService TagContactRemove(%s, %s)\n", id, contactID)
_, err = p.grpcClient.TagContactRemove(context.Background(), &pbwechatypuppet.TagContactRemoveRequest{
Id: id,
ContactId: contactID,
})
return err
}
// TagContactDelete ...
func (p *PuppetService) TagContactDelete(id string) (err error) {
log.Tracef("PuppetService TagContactDelete(%s)\n", id)
_, err = p.grpcClient.TagContactDelete(context.Background(), &pbwechatypuppet.TagContactDeleteRequest{
Id: id,
})
return err
}
// TagContactList ...
func (p *PuppetService) TagContactList(contactID string) ([]string, error) {
log.Tracef("PuppetService TagContactList(%s)\n", contactID)
request := &pbwechatypuppet.TagContactListRequest{}
if contactID != "" {
request.ContactId = contactID
}
response, err := p.grpcClient.TagContactList(context.Background(), request)
if err != nil {
return nil, err
}
return response.Ids, nil
}
// DirtyPayload ...
func (p *PuppetService) DirtyPayload(payloadType schemas.PayloadType, id string) error {
log.Tracef("PuppetService DirtyPayload(%v, %v)\n", payloadType, id)
err := p.Puppet.OnDirty(payloadType, id)
if err != nil {
return err
}
request := &pbwechatypuppet.DirtyPayloadRequest{
Type: pbwechatypuppet.PayloadType(payloadType),
Id: id,
}
_, err = p.grpcClient.DirtyPayload(context.Background(), request)
if err != nil {
return err
}
return nil
}
// MessageForward message forward
func (p *PuppetService) MessageForward(conversationID string, messageID string) (string, error) {
log.Tracef("PuppetService MessageForward(%v, %v)\n", conversationID, messageID)
request := &pbwechatypuppet.MessageForwardRequest{
MessageId: messageID,
ConversationId: conversationID,
}
response, err := p.grpcClient.MessageForward(context.Background(), request)
if err != nil {
return "", err
}
return response.Id, nil
}