Support pending updates

This commit is contained in:
c0re100 2022-02-06 11:22:20 +08:00
parent a339293774
commit 6400f59b1b
No known key found for this signature in database
GPG key ID: 7C3B3004FE745AAF
2 changed files with 179 additions and 28 deletions

View file

@ -9,10 +9,13 @@ import (
"time" "time"
) )
var pendingUpdateType []Type
type Client struct { type Client struct {
jsonClient *JsonClient jsonClient *JsonClient
extraGenerator ExtraGenerator extraGenerator ExtraGenerator
responses chan *Response responses chan *Response
pendingResp chan *Response
listenerStore *listenerStore listenerStore *listenerStore
catchersStore *sync.Map catchersStore *sync.Map
successMsgStore *sync.Map successMsgStore *sync.Map
@ -56,10 +59,18 @@ func SetFilePath(path string) {
}) })
} }
// Keep specific update type in memory when listener is not ready.
func SetPendingUpdateType(update ...Type) {
for _, v := range update {
pendingUpdateType = append(pendingUpdateType, v)
}
}
func NewClient(authorizationStateHandler AuthorizationStateHandler, options ...Option) (*Client, error) { func NewClient(authorizationStateHandler AuthorizationStateHandler, options ...Option) (*Client, error) {
client := &Client{ client := &Client{
jsonClient: NewJsonClient(), jsonClient: NewJsonClient(),
responses: make(chan *Response, 1000), responses: make(chan *Response, 1000),
pendingResp: make(chan *Response, 1000),
listenerStore: newListenerStore(), listenerStore: newListenerStore(),
catchersStore: &sync.Map{}, catchersStore: &sync.Map{},
successMsgStore: &sync.Map{}, successMsgStore: &sync.Map{},
@ -74,6 +85,7 @@ func NewClient(authorizationStateHandler AuthorizationStateHandler, options ...O
tdlibInstance.addClient(client) tdlibInstance.addClient(client)
go client.processPendingResponse()
go client.receiver() go client.receiver()
err := Authorize(client, authorizationStateHandler) err := Authorize(client, authorizationStateHandler)
@ -84,40 +96,72 @@ func NewClient(authorizationStateHandler AuthorizationStateHandler, options ...O
return client, nil return client, nil
} }
func (client *Client) processResponse(response *Response) {
if response.Extra != "" {
value, ok := client.catchersStore.Load(response.Extra)
if ok {
value.(chan *Response) <- response
}
}
typ, err := UnmarshalType(response.Data)
if err != nil {
return
}
if typ.GetType() == (&UpdateMessageSendSucceeded{}).GetType() {
value, ok := client.successMsgStore.Load(typ.(*UpdateMessageSendSucceeded).OldMessageId)
if ok {
value.(chan *Response) <- response
}
}
if len(client.listenerStore.Listeners()) == 0 {
for _, p := range pendingUpdateType {
if typ.GetType() == p.GetType() {
client.pendingResp <- response
}
}
}
needGc := false
for _, listener := range client.listenerStore.Listeners() {
if listener.IsActive() && listener.Updates != nil && typ.GetType() == listener.Filter.GetType() { // All updates go to Updates channel if type == filter
listener.Updates <- typ
} else if listener.IsActive() && listener.RawUpdates != nil { // All updates go to RawUpdates channel if filter is empty
listener.RawUpdates <- typ
} else if !listener.IsActive() { // GC inactive listener
needGc = true
}
}
if needGc {
client.listenerStore.gc()
}
}
func (client *Client) receiver() { func (client *Client) receiver() {
for response := range client.responses { for response := range client.responses {
if response.Extra != "" { client.processResponse(response)
value, ok := client.catchersStore.Load(response.Extra) }
if ok { }
value.(chan *Response) <- response
}
}
typ, err := UnmarshalType(response.Data) func (client *Client) processPendingResponse() {
if err != nil { // No need to process pending response if no pending list.
continue if len(pendingUpdateType) == 0 {
} return
}
if typ.GetType() == (&UpdateMessageSendSucceeded{}).GetType() { // Wait for listener to be ready.
value, ok := client.successMsgStore.Load(typ.(*UpdateMessageSendSucceeded).OldMessageId) for {
if ok { if len(client.listenerStore.Listeners()) > 0 {
value.(chan *Response) <- response break
}
} }
time.Sleep(1 * time.Second)
}
needGc := false // Start processing pending response
for _, listener := range client.listenerStore.Listeners() { for response := range client.pendingResp {
if listener.IsActive() && listener.Updates != nil && typ.GetType() == listener.Filter.GetType() { // All updates go to Updates channel if type == filter client.processResponse(response)
listener.Updates <- typ
} else if listener.IsActive() && listener.RawUpdates != nil { // All updates go to RawUpdates channel if filter is empty
listener.RawUpdates <- typ
} else if !listener.IsActive() { // GC inactive listener
needGc = true
}
}
if needGc {
client.listenerStore.gc()
}
} }
} }

View file

@ -0,0 +1,107 @@
package main
import (
"log"
"os"
"os/signal"
"syscall"
tdlib "github.com/c0re100/gotdlib/client"
)
func GetTdParameters() *tdlib.TdlibParameters {
return &tdlib.TdlibParameters{
UseTestDc: false,
DatabaseDirectory: "./tdlib-db",
FilesDirectory: "./tdlib-files",
UseFileDatabase: true,
UseChatInfoDatabase: true,
UseMessageDatabase: true,
UseSecretChats: false,
ApiId: 132712,
ApiHash: "e82c07ad653399a37baca8d1e498e472",
SystemLanguageCode: "en",
DeviceModel: "HuskyNG",
SystemVersion: "3.0",
ApplicationVersion: "3.0",
EnableStorageOptimizer: true,
IgnoreFileNames: false,
}
}
func main() {
tdlib.SetLogLevel(0)
tdlib.SetFilePath("./errors.txt")
// Set pending update list
tdlib.SetPendingUpdateType(&tdlib.UpdateNewMessage{})
// Of coz, you can set more than one type, depending on your needs
//tdlib.SetPendingUpdateType(&tdlib.UpdateNewMessage{}, &tdlib.UpdateMessageEdited{}, &tdlib.UpdateDeleteMessages{})
botToken := "your_bot_token"
authorizer := tdlib.BotAuthorizer(botToken)
authorizer.TdlibParameters <- GetTdParameters()
client, err := tdlib.NewClient(authorizer)
if err != nil {
log.Fatalf("NewClient error: %s", err)
}
// Handle SIGINT
ch := make(chan os.Signal, 2)
signal.Notify(ch, os.Interrupt, syscall.SIGINT)
signal.Notify(ch, os.Interrupt, syscall.SIGKILL)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
signal.Notify(ch, os.Interrupt, syscall.SIGQUIT)
signal.Notify(ch, os.Interrupt, syscall.SIGSEGV)
go func() {
<-ch
client.Close()
}()
me, err := client.GetMe()
if err != nil {
log.Fatalf("GetMe error: %s", err)
}
log.Printf("%s connected", me.Username)
listener := client.AddEventReceiver(&tdlib.UpdateNewMessage{}, 1000)
defer listener.Close()
for update := range listener.Updates {
updateMsg := update.(*tdlib.UpdateNewMessage)
chatId := updateMsg.Message.ChatId
msgId := updateMsg.Message.Id
var msgText string
var msgEnt []*tdlib.TextEntity
switch updateMsg.Message.Content.MessageContentType() {
case "messageText":
msgText = updateMsg.Message.Content.(*tdlib.MessageText).Text.Text
msgEnt = updateMsg.Message.Content.(*tdlib.MessageText).Text.Entities
cmd := tdlib.CheckCommand(msgText, msgEnt)
switch cmd {
case "/ping":
text, _ := tdlib.ParseTextEntities(&tdlib.ParseTextEntitiesRequest{
Text: "<b>pong!</b>",
ParseMode: &tdlib.TextParseModeHTML{},
})
m, err := client.SendMessage(&tdlib.SendMessageRequest{
ChatId: chatId,
ReplyToMessageId: msgId,
InputMessageContent: &tdlib.InputMessageText{
Text: text,
},
})
if err != nil {
continue
}
log.Printf("Message sent, ID: %d", m.Id)
}
}
}
}