парсер
This commit is contained in:
37
tg_parser/internal/adapter/telegram/get_active_views.go
Normal file
37
tg_parser/internal/adapter/telegram/get_active_views.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/TelegramExchange/tgex-parser/internal/domain"
|
||||
"github.com/gotd/td/tg"
|
||||
)
|
||||
|
||||
func (t *Telegram) UpdatePostsViews(ctx context.Context, channel domain.Channel, posts []domain.Post) error {
|
||||
ids := make([]int, len(posts))
|
||||
for i, p := range posts {
|
||||
ids[i] = p.MessageID
|
||||
}
|
||||
|
||||
req := &tg.MessagesGetMessagesViewsRequest{
|
||||
Peer: &tg.InputPeerChannel{
|
||||
ChannelID: channel.ChannelID(),
|
||||
AccessHash: channel.AccessHash,
|
||||
},
|
||||
ID: ids,
|
||||
Increment: false,
|
||||
}
|
||||
|
||||
resp, err := t.API().MessagesGetMessagesViews(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get messages views: %w", err)
|
||||
}
|
||||
|
||||
// Telegram гарантирует, что resp.Views соответствует порядку ids
|
||||
for i, v := range resp.Views {
|
||||
posts[i].Views = v.Views
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
119
tg_parser/internal/adapter/telegram/get_channel_diff.go
Normal file
119
tg_parser/internal/adapter/telegram/get_channel_diff.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/TelegramExchange/tgex-parser/internal/domain"
|
||||
"github.com/gotd/td/tg"
|
||||
)
|
||||
|
||||
func (t *Telegram) GetChannelDiff(ctx context.Context, channel domain.Channel, limit int) (domain.ChannelDiff, error) {
|
||||
req := &tg.UpdatesGetChannelDifferenceRequest{
|
||||
Channel: &tg.InputChannel{
|
||||
ChannelID: channel.ChannelID(),
|
||||
AccessHash: channel.AccessHash,
|
||||
},
|
||||
Filter: &tg.ChannelMessagesFilterEmpty{},
|
||||
Pts: channel.Pts,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
rawDiff, err := t.API().UpdatesGetChannelDifference(ctx, req)
|
||||
if err != nil {
|
||||
return domain.ChannelDiff{}, fmt.Errorf("get difference: %w", err)
|
||||
}
|
||||
|
||||
result := domain.ChannelDiff{}
|
||||
|
||||
switch d := rawDiff.(type) {
|
||||
case *tg.UpdatesChannelDifferenceEmpty:
|
||||
result.NewPts = d.Pts
|
||||
|
||||
case *tg.UpdatesChannelDifferenceTooLong:
|
||||
if dialog, ok := d.Dialog.(*tg.Dialog); ok {
|
||||
result.NewPts = dialog.Pts
|
||||
}
|
||||
result.NewPosts = extractPosts(channel, d.Messages)
|
||||
result.UpdatedChannel = extractChannelMeta(channel, d.Chats)
|
||||
|
||||
case *tg.UpdatesChannelDifference:
|
||||
result.NewPts = d.Pts
|
||||
result.NewPosts = extractPosts(channel, d.NewMessages)
|
||||
result.DeletedPosts = extractDeletedPosts(channel, d.OtherUpdates)
|
||||
result.UpdatedChannel = extractChannelMeta(channel, d.Chats)
|
||||
|
||||
default:
|
||||
return domain.ChannelDiff{}, fmt.Errorf("unexpected rawDiff type: %T", rawDiff)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func extractPosts(channel domain.Channel, msgs []tg.MessageClass) []domain.Post {
|
||||
posts := make([]domain.Post, 0, len(msgs))
|
||||
|
||||
for _, raw := range msgs {
|
||||
m, ok := raw.(*tg.Message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p := domain.NewPost(channel, m.ID, m.Message, m.Views)
|
||||
posts = append(posts, p)
|
||||
}
|
||||
|
||||
return posts
|
||||
}
|
||||
|
||||
func extractDeletedPosts(channel domain.Channel, updates []tg.UpdateClass) []domain.Post {
|
||||
var deleted []domain.Post
|
||||
|
||||
for _, upd := range updates {
|
||||
if u, ok := upd.(*tg.UpdateDeleteChannelMessages); ok {
|
||||
for _, msgID := range u.Messages {
|
||||
p := domain.NewPost(channel, msgID, "", 0)
|
||||
deleted = append(deleted, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deleted
|
||||
}
|
||||
|
||||
func extractChannelMeta(currentChannel domain.Channel, chats []tg.ChatClass) *domain.Channel {
|
||||
for _, chat := range chats {
|
||||
ch, ok := chat.(*tg.Channel)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ch.ID != currentChannel.ChannelID() {
|
||||
continue
|
||||
}
|
||||
|
||||
username := ""
|
||||
if usernameVal, ok := ch.GetUsername(); ok {
|
||||
username = usernameVal
|
||||
}
|
||||
|
||||
// Keep current access hash if new one is not provided or is zero
|
||||
accessHash := currentChannel.AccessHash
|
||||
if accessHashVal, ok := ch.GetAccessHash(); ok && accessHashVal != 0 {
|
||||
accessHash = accessHashVal
|
||||
}
|
||||
|
||||
updated := domain.Channel{
|
||||
ID: currentChannel.ID,
|
||||
TelegramID: domain.ChatIDFromChannelID(ch.ID),
|
||||
Username: username,
|
||||
Title: ch.Title,
|
||||
AccessHash: accessHash,
|
||||
Pts: currentChannel.Pts,
|
||||
}
|
||||
|
||||
return &updated
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
46
tg_parser/internal/adapter/telegram/history.go
Normal file
46
tg_parser/internal/adapter/telegram/history.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/TelegramExchange/tgex-parser/internal/domain"
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (t *Telegram) GetChannelHistory(ctx context.Context, channel domain.Channel, limit int) ([]domain.Post, error) {
|
||||
log.Info().Msg(channel.String())
|
||||
|
||||
req := &tg.MessagesGetHistoryRequest{
|
||||
Peer: &tg.InputPeerChannel{
|
||||
ChannelID: channel.ChannelID(),
|
||||
AccessHash: channel.AccessHash,
|
||||
},
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
resp, err := t.API().MessagesGetHistory(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resp: %w", err)
|
||||
}
|
||||
|
||||
messages, ok := resp.(*tg.MessagesChannelMessages)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
placements := make([]domain.Post, 0, len(messages.Messages))
|
||||
|
||||
for _, raw := range messages.Messages {
|
||||
m, ok := raw.(*tg.Message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p := domain.NewPost(channel, m.ID, m.Message, m.Views)
|
||||
placements = append(placements, p)
|
||||
}
|
||||
|
||||
return placements, nil
|
||||
}
|
||||
59
tg_parser/internal/adapter/telegram/resolve.go
Normal file
59
tg_parser/internal/adapter/telegram/resolve.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/TelegramExchange/tgex-parser/internal/domain"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gotd/td/tg"
|
||||
)
|
||||
|
||||
func (t *Telegram) ParseChannelMeta(ctx context.Context, username string) (domain.Channel, error) {
|
||||
resolved, err := t.API().ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
|
||||
Username: username,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.Channel{}, fmt.Errorf("resolve username: %w", err)
|
||||
}
|
||||
|
||||
if len(resolved.Chats) == 0 {
|
||||
return domain.Channel{}, fmt.Errorf("no chats in resolve result")
|
||||
}
|
||||
|
||||
ch, ok := resolved.Chats[0].(*tg.Channel)
|
||||
if !ok {
|
||||
return domain.Channel{}, fmt.Errorf("not a channel")
|
||||
}
|
||||
|
||||
req := []tg.InputDialogPeerClass{
|
||||
&tg.InputDialogPeer{
|
||||
Peer: &tg.InputPeerChannel{
|
||||
ChannelID: ch.ID,
|
||||
AccessHash: ch.AccessHash,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dialogs, err := t.API().MessagesGetPeerDialogs(ctx, req)
|
||||
if err != nil {
|
||||
return domain.Channel{}, fmt.Errorf("peer dialogs: %w", err)
|
||||
}
|
||||
|
||||
pts := 0
|
||||
if len(dialogs.Dialogs) > 0 {
|
||||
d, ok := dialogs.Dialogs[0].(*tg.Dialog)
|
||||
if ok {
|
||||
pts = d.Pts
|
||||
}
|
||||
}
|
||||
|
||||
return domain.Channel{
|
||||
ID: uuid.New(),
|
||||
TelegramID: domain.ChatIDFromChannelID(ch.ID),
|
||||
Username: username,
|
||||
Title: ch.Title,
|
||||
AccessHash: ch.AccessHash,
|
||||
Pts: pts,
|
||||
}, nil
|
||||
}
|
||||
20
tg_parser/internal/adapter/telegram/telegram.go
Normal file
20
tg_parser/internal/adapter/telegram/telegram.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"github.com/TelegramExchange/tgex-parser/pkg/telegram"
|
||||
"github.com/gotd/td/tg"
|
||||
)
|
||||
|
||||
type Telegram struct {
|
||||
client *telegram.Client
|
||||
}
|
||||
|
||||
func New(client *telegram.Client) *Telegram {
|
||||
return &Telegram{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Telegram) API() *tg.Client {
|
||||
return t.client.API()
|
||||
}
|
||||
Reference in New Issue
Block a user