client.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. package telegram
  2. import (
  3. "bufio"
  4. "context"
  5. "encoding/base64"
  6. "fmt"
  7. "log"
  8. "net"
  9. "net/url"
  10. "regexp"
  11. "sort"
  12. "strings"
  13. "sync"
  14. "time"
  15. "github.com/gotd/td/session"
  16. "github.com/gotd/td/telegram"
  17. "github.com/gotd/td/telegram/dcs"
  18. "github.com/gotd/td/tg"
  19. "github.com/gotd/td/tgerr"
  20. xproxy "golang.org/x/net/proxy"
  21. )
  22. var tmeRegexp = regexp.MustCompile(`https?://t\.me/[^\s"'<>)\]]+`)
  23. // Client TG 客户端
  24. type Client struct {
  25. account Account
  26. sessionPath string
  27. proxyURL string // SOCKS5/HTTP proxy URL
  28. mu sync.Mutex
  29. tgc *telegram.Client
  30. api *tg.Client
  31. cancel context.CancelFunc
  32. ready chan struct{} // closed when connected
  33. runErr error
  34. }
  35. // New 创建客户端(不连接,只初始化)
  36. func New(account Account) *Client {
  37. return &Client{
  38. account: account,
  39. sessionPath: account.SessionFile,
  40. ready: make(chan struct{}),
  41. }
  42. }
  43. // SetProxy sets the proxy URL for this client's connections.
  44. func (c *Client) SetProxy(proxyURL string) {
  45. c.mu.Lock()
  46. c.proxyURL = proxyURL
  47. c.mu.Unlock()
  48. }
  49. // Connect 连接并认证(从 session 文件恢复)
  50. // session 文件不存在时返回错误(不做交互式登录,session 需要预先生成)
  51. func (c *Client) Connect(ctx context.Context) error {
  52. storage := &session.FileStorage{Path: c.sessionPath}
  53. opts := telegram.Options{
  54. SessionStorage: storage,
  55. NoUpdates: true,
  56. Device: telegram.DeviceConfig{
  57. DeviceModel: c.account.Device,
  58. AppVersion: c.account.AppVersion,
  59. SystemVersion: c.account.SystemVersion,
  60. LangPack: c.account.LangPack,
  61. SystemLangCode: c.account.SystemLangCode,
  62. LangCode: c.account.LangCode,
  63. },
  64. }
  65. // Apply proxy if configured
  66. c.mu.Lock()
  67. proxyURL := c.proxyURL
  68. c.mu.Unlock()
  69. if proxyURL != "" {
  70. dialFunc, err := buildProxyDialer(proxyURL)
  71. if err != nil {
  72. log.Printf("[tg_client] failed to create proxy dialer: %v, connecting without proxy", err)
  73. } else {
  74. opts.Resolver = dcs.Plain(dcs.PlainOptions{Dial: dialFunc})
  75. log.Printf("[tg_client] connecting via proxy: %s", proxyURL)
  76. }
  77. }
  78. client := telegram.NewClient(c.account.AppID, c.account.AppHash, opts)
  79. runCtx, cancel := context.WithCancel(ctx)
  80. c.mu.Lock()
  81. c.tgc = client
  82. c.cancel = cancel
  83. c.ready = make(chan struct{})
  84. c.runErr = nil
  85. readyCh := c.ready
  86. c.mu.Unlock()
  87. errCh := make(chan error, 1)
  88. go func() {
  89. err := client.Run(runCtx, func(ctx context.Context) error {
  90. c.mu.Lock()
  91. c.api = client.API()
  92. close(readyCh)
  93. c.mu.Unlock()
  94. // Block until context is cancelled (Disconnect called)
  95. <-ctx.Done()
  96. return ctx.Err()
  97. })
  98. c.mu.Lock()
  99. c.runErr = err
  100. c.mu.Unlock()
  101. errCh <- err
  102. }()
  103. // Wait for ready or error
  104. select {
  105. case <-readyCh:
  106. return nil
  107. case err := <-errCh:
  108. if err != nil && err != context.Canceled {
  109. return err
  110. }
  111. return nil
  112. case <-ctx.Done():
  113. cancel()
  114. return ctx.Err()
  115. }
  116. }
  117. // Disconnect 断开连接
  118. func (c *Client) Disconnect() {
  119. c.mu.Lock()
  120. cancel := c.cancel
  121. c.mu.Unlock()
  122. if cancel != nil {
  123. cancel()
  124. }
  125. }
  126. // waitReady waits for the client to be connected and returns the api client
  127. func (c *Client) waitReady(ctx context.Context) (*tg.Client, error) {
  128. c.mu.Lock()
  129. readyCh := c.ready
  130. api := c.api
  131. c.mu.Unlock()
  132. if api != nil {
  133. return api, nil
  134. }
  135. select {
  136. case <-readyCh:
  137. c.mu.Lock()
  138. api = c.api
  139. c.mu.Unlock()
  140. return api, nil
  141. case <-ctx.Done():
  142. return nil, ctx.Err()
  143. }
  144. }
  145. // GetChannelInfo 获取频道/用户信息,通过用户名查找
  146. func (c *Client) GetChannelInfo(ctx context.Context, username string) (*ChannelInfo, error) {
  147. api, err := c.waitReady(ctx)
  148. if err != nil {
  149. return nil, err
  150. }
  151. username = strings.TrimPrefix(username, "@")
  152. resolved, err := api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
  153. Username: username,
  154. })
  155. if err != nil {
  156. return nil, wrapFloodWait(err)
  157. }
  158. info := &ChannelInfo{Username: username}
  159. // Look for channel/chat in the resolved chats
  160. for _, ch := range resolved.Chats {
  161. switch v := ch.(type) {
  162. case *tg.Channel:
  163. title := v.Title
  164. info.Title = title
  165. info.IsChannel = v.GetBroadcast()
  166. info.IsGroup = v.GetMegagroup()
  167. if count, ok := v.GetParticipantsCount(); ok {
  168. info.MemberCount = count
  169. }
  170. // Get full channel info for About
  171. accessHash, hasHash := v.GetAccessHash()
  172. if hasHash {
  173. full, ferr := api.ChannelsGetFullChannel(ctx, &tg.InputChannel{
  174. ChannelID: v.GetID(),
  175. AccessHash: accessHash,
  176. })
  177. if ferr == nil {
  178. if cf, ok := full.FullChat.(*tg.ChannelFull); ok {
  179. info.About = cf.GetAbout()
  180. if count, ok := cf.GetParticipantsCount(); ok && info.MemberCount == 0 {
  181. info.MemberCount = count
  182. }
  183. }
  184. }
  185. }
  186. return info, nil
  187. case *tg.Chat:
  188. info.Title = v.Title
  189. info.IsGroup = true
  190. info.MemberCount = v.ParticipantsCount
  191. return info, nil
  192. }
  193. }
  194. return info, nil
  195. }
  196. // GetMessages 获取频道历史消息
  197. // offsetID: 从哪条消息开始(断点续传)
  198. // limit: 最多取多少条
  199. // 返回的消息按 ID 从小到大排序
  200. func (c *Client) GetMessages(ctx context.Context, username string, offsetID, limit int) ([]Message, error) {
  201. api, err := c.waitReady(ctx)
  202. if err != nil {
  203. return nil, err
  204. }
  205. username = strings.TrimPrefix(username, "@")
  206. peer, err := c.resolveInputPeer(ctx, api, username)
  207. if err != nil {
  208. return nil, err
  209. }
  210. result, err := api.MessagesGetHistory(ctx, &tg.MessagesGetHistoryRequest{
  211. Peer: peer,
  212. OffsetID: offsetID,
  213. Limit: limit,
  214. })
  215. if err != nil {
  216. return nil, wrapFloodWait(err)
  217. }
  218. return extractMessages(result), nil
  219. }
  220. // GetPinnedMessages 获取置顶消息
  221. func (c *Client) GetPinnedMessages(ctx context.Context, username string) ([]Message, error) {
  222. api, err := c.waitReady(ctx)
  223. if err != nil {
  224. return nil, err
  225. }
  226. username = strings.TrimPrefix(username, "@")
  227. peer, err := c.resolveInputPeer(ctx, api, username)
  228. if err != nil {
  229. return nil, err
  230. }
  231. result, err := api.MessagesSearch(ctx, &tg.MessagesSearchRequest{
  232. Peer: peer,
  233. Filter: &tg.InputMessagesFilterPinned{},
  234. Limit: 100,
  235. })
  236. if err != nil {
  237. return nil, wrapFloodWait(err)
  238. }
  239. return extractMessages(result), nil
  240. }
  241. // VerifyUser 验证用户名是否存在,返回用户信息
  242. func (c *Client) VerifyUser(ctx context.Context, username string) (*UserInfo, error) {
  243. api, err := c.waitReady(ctx)
  244. if err != nil {
  245. return nil, err
  246. }
  247. username = strings.TrimPrefix(username, "@")
  248. resolved, err := api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
  249. Username: username,
  250. })
  251. if err != nil {
  252. if tgerr.Is(err, "USERNAME_NOT_OCCUPIED", "USERNAME_INVALID") {
  253. return &UserInfo{Username: username, Exists: false}, nil
  254. }
  255. return nil, wrapFloodWait(err)
  256. }
  257. // Check if the peer is a user
  258. if _, ok := resolved.Peer.(*tg.PeerUser); ok {
  259. for _, u := range resolved.Users {
  260. if user, ok := u.(*tg.User); ok {
  261. info := &UserInfo{
  262. ID: user.GetID(),
  263. Username: username,
  264. IsBot: user.GetBot(),
  265. IsPremium: user.GetPremium(),
  266. Exists: true,
  267. }
  268. if fn, ok := user.GetFirstName(); ok {
  269. info.FirstName = fn
  270. }
  271. if ln, ok := user.GetLastName(); ok {
  272. info.LastName = ln
  273. }
  274. if status, ok := user.GetStatus(); ok {
  275. if offline, ok := status.(*tg.UserStatusOffline); ok {
  276. t := time.Unix(int64(offline.GetWasOnline()), 0)
  277. info.LastOnline = &t
  278. }
  279. }
  280. return info, nil
  281. }
  282. }
  283. }
  284. // Check if it's a channel or group
  285. for _, ch := range resolved.Chats {
  286. switch v := ch.(type) {
  287. case *tg.Channel:
  288. return &UserInfo{
  289. ID: v.GetID(),
  290. Username: username,
  291. IsChannel: v.GetBroadcast(),
  292. IsGroup: v.GetMegagroup(),
  293. Exists: true,
  294. }, nil
  295. case *tg.Chat:
  296. return &UserInfo{
  297. ID: v.ID,
  298. IsGroup: true,
  299. Exists: true,
  300. }, nil
  301. }
  302. }
  303. return &UserInfo{Username: username, Exists: false}, nil
  304. }
  305. // GetGroupParticipants 获取群组/超级群组的成员列表(分页拉取全部)
  306. func (c *Client) GetGroupParticipants(ctx context.Context, username string) ([]GroupParticipant, error) {
  307. api, err := c.waitReady(ctx)
  308. if err != nil {
  309. return nil, err
  310. }
  311. username = strings.TrimPrefix(username, "@")
  312. // Resolve the channel/group
  313. resolved, err := api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
  314. Username: username,
  315. })
  316. if err != nil {
  317. return nil, wrapFloodWait(err)
  318. }
  319. // Find the channel in resolved chats
  320. var inputChannel *tg.InputChannel
  321. for _, ch := range resolved.Chats {
  322. switch v := ch.(type) {
  323. case *tg.Channel:
  324. accessHash, _ := v.GetAccessHash()
  325. inputChannel = &tg.InputChannel{
  326. ChannelID: v.GetID(),
  327. AccessHash: accessHash,
  328. }
  329. }
  330. }
  331. if inputChannel == nil {
  332. // Try as basic chat - get participants via MessagesGetFullChat
  333. if p, ok := resolved.Peer.(*tg.PeerChat); ok {
  334. return c.getChatParticipants(ctx, api, p.ChatID)
  335. }
  336. return nil, fmt.Errorf("无法解析群组: %s", username)
  337. }
  338. // Strategy: use ChannelParticipantsSearch with empty query (returns more than Recent),
  339. // then iterate alphabet queries to discover members beyond the 200 limit per query.
  340. seen := make(map[int64]bool)
  341. var allParticipants []GroupParticipant
  342. // Helper to extract users from a page
  343. extractUsers := func(cp *tg.ChannelsChannelParticipants) int {
  344. added := 0
  345. for _, u := range cp.Users {
  346. user, ok := u.(*tg.User)
  347. if !ok || seen[user.GetID()] {
  348. continue
  349. }
  350. seen[user.GetID()] = true
  351. p := GroupParticipant{
  352. ID: user.GetID(),
  353. IsBot: user.GetBot(),
  354. IsPremium: user.GetPremium(),
  355. }
  356. if un, ok := user.GetUsername(); ok {
  357. p.Username = un
  358. }
  359. if fn, ok := user.GetFirstName(); ok {
  360. p.FirstName = fn
  361. }
  362. if ln, ok := user.GetLastName(); ok {
  363. p.LastName = ln
  364. }
  365. allParticipants = append(allParticipants, p)
  366. added++
  367. }
  368. return added
  369. }
  370. // Phase 1: Search with empty query (gets up to ~200)
  371. totalCount := 0
  372. if err := c.fetchParticipantPages(ctx, api, inputChannel, "", seen, extractUsers, &totalCount); err != nil {
  373. if len(allParticipants) > 0 {
  374. return allParticipants, err
  375. }
  376. return nil, err
  377. }
  378. // Phase 2: If group has more members than we found, search by character sets to discover more
  379. if totalCount > len(allParticipants) && totalCount <= 10000 {
  380. queries := participantSearchQueries()
  381. for _, q := range queries {
  382. if ctx.Err() != nil {
  383. break
  384. }
  385. beforeCount := len(allParticipants)
  386. _ = c.fetchParticipantPages(ctx, api, inputChannel, q, seen, extractUsers, nil)
  387. if len(allParticipants) == beforeCount {
  388. continue // No new results for this query
  389. }
  390. select {
  391. case <-ctx.Done():
  392. return allParticipants, ctx.Err()
  393. case <-time.After(300 * time.Millisecond):
  394. }
  395. }
  396. }
  397. log.Printf("[tg_client] fetched %d/%d participants for %s", len(allParticipants), totalCount, username)
  398. return allParticipants, nil
  399. }
  400. // participantSearchQueries returns search queries covering Latin, Cyrillic, CJK and other scripts.
  401. func participantSearchQueries() []string {
  402. queries := make([]string, 0, 80)
  403. // Latin a-z
  404. for c := 'a'; c <= 'z'; c++ {
  405. queries = append(queries, string(c))
  406. }
  407. // Digits 0-9
  408. for c := '0'; c <= '9'; c++ {
  409. queries = append(queries, string(c))
  410. }
  411. // Cyrillic а-я
  412. for c := 'а'; c <= 'я'; c++ {
  413. queries = append(queries, string(c))
  414. }
  415. // Common CJK first characters (high frequency Chinese surnames and words)
  416. cjk := []string{"王", "李", "张", "刘", "陈", "杨", "黄", "赵", "周", "吴",
  417. "徐", "孙", "马", "朱", "胡", "林", "何", "高", "郭", "罗",
  418. "大", "小", "新", "老", "中", "天", "金", "一"}
  419. queries = append(queries, cjk...)
  420. return queries
  421. }
  422. // fetchParticipantPages paginates through ChannelParticipantsSearch results.
  423. func (c *Client) fetchParticipantPages(
  424. ctx context.Context,
  425. api *tg.Client,
  426. channel *tg.InputChannel,
  427. query string,
  428. seen map[int64]bool,
  429. extractUsers func(*tg.ChannelsChannelParticipants) int,
  430. outTotalCount *int,
  431. ) error {
  432. const pageSize = 200
  433. offset := 0
  434. for {
  435. result, err := api.ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
  436. Channel: channel,
  437. Filter: &tg.ChannelParticipantsSearch{Q: query},
  438. Offset: offset,
  439. Limit: pageSize,
  440. Hash: 0,
  441. })
  442. if err != nil {
  443. return wrapFloodWait(err)
  444. }
  445. cp, ok := result.(*tg.ChannelsChannelParticipants)
  446. if !ok || len(cp.Users) == 0 {
  447. break
  448. }
  449. if outTotalCount != nil && cp.Count > *outTotalCount {
  450. *outTotalCount = cp.Count
  451. }
  452. added := extractUsers(cp)
  453. offset += len(cp.Users)
  454. // If no new users were added in this page, stop
  455. if added == 0 || offset >= cp.Count {
  456. break
  457. }
  458. select {
  459. case <-ctx.Done():
  460. return ctx.Err()
  461. case <-time.After(500 * time.Millisecond):
  462. }
  463. }
  464. return nil
  465. }
  466. // getChatParticipants 获取普通群组的成员
  467. func (c *Client) getChatParticipants(ctx context.Context, api *tg.Client, chatID int64) ([]GroupParticipant, error) {
  468. full, err := api.MessagesGetFullChat(ctx, chatID)
  469. if err != nil {
  470. return nil, wrapFloodWait(err)
  471. }
  472. var participants []GroupParticipant
  473. for _, u := range full.Users {
  474. user, ok := u.(*tg.User)
  475. if !ok {
  476. continue
  477. }
  478. p := GroupParticipant{
  479. ID: user.GetID(),
  480. IsBot: user.GetBot(),
  481. IsPremium: user.GetPremium(),
  482. }
  483. if un, ok := user.GetUsername(); ok {
  484. p.Username = un
  485. }
  486. if fn, ok := user.GetFirstName(); ok {
  487. p.FirstName = fn
  488. }
  489. if ln, ok := user.GetLastName(); ok {
  490. p.LastName = ln
  491. }
  492. participants = append(participants, p)
  493. }
  494. return participants, nil
  495. }
  496. // buildProxyDialer creates a DialFunc that routes connections through the given proxy URL.
  497. // Supports socks5://, http://, https:// proxy protocols.
  498. func buildProxyDialer(rawURL string) (dcs.DialFunc, error) {
  499. u, err := url.Parse(rawURL)
  500. if err != nil {
  501. return nil, fmt.Errorf("parse proxy URL: %w", err)
  502. }
  503. switch u.Scheme {
  504. case "socks5", "socks5h":
  505. var auth *xproxy.Auth
  506. if u.User != nil {
  507. auth = &xproxy.Auth{User: u.User.Username()}
  508. if p, ok := u.User.Password(); ok {
  509. auth.Password = p
  510. }
  511. }
  512. dialer, err := xproxy.SOCKS5("tcp", u.Host, auth, xproxy.Direct)
  513. if err != nil {
  514. return nil, fmt.Errorf("create SOCKS5 dialer: %w", err)
  515. }
  516. ctxDialer, ok := dialer.(xproxy.ContextDialer)
  517. if !ok {
  518. return nil, fmt.Errorf("SOCKS5 dialer does not support DialContext")
  519. }
  520. return ctxDialer.DialContext, nil
  521. case "http", "https":
  522. // For HTTP proxies, use CONNECT tunneling
  523. return func(ctx context.Context, network, addr string) (net.Conn, error) {
  524. proxyConn, err := (&net.Dialer{Timeout: 15 * time.Second}).DialContext(ctx, "tcp", u.Host)
  525. if err != nil {
  526. return nil, fmt.Errorf("connect to proxy: %w", err)
  527. }
  528. // Set deadline for the CONNECT handshake
  529. if deadline, ok := ctx.Deadline(); ok {
  530. proxyConn.SetDeadline(deadline)
  531. } else {
  532. proxyConn.SetDeadline(time.Now().Add(15 * time.Second))
  533. }
  534. // Send CONNECT request
  535. connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\n", addr, addr)
  536. if u.User != nil {
  537. pass, _ := u.User.Password()
  538. connectReq += fmt.Sprintf("Proxy-Authorization: Basic %s\r\n",
  539. encodeBasicAuth(u.User.Username(), pass))
  540. }
  541. connectReq += "\r\n"
  542. if _, err := proxyConn.Write([]byte(connectReq)); err != nil {
  543. proxyConn.Close()
  544. return nil, fmt.Errorf("write CONNECT: %w", err)
  545. }
  546. // Read HTTP response using bufio for proper line parsing
  547. br := bufio.NewReader(proxyConn)
  548. statusLine, err := br.ReadString('\n')
  549. if err != nil {
  550. proxyConn.Close()
  551. return nil, fmt.Errorf("read CONNECT status: %w", err)
  552. }
  553. // Parse status code from "HTTP/1.x NNN reason"
  554. parts := strings.SplitN(strings.TrimSpace(statusLine), " ", 3)
  555. if len(parts) < 2 || parts[1] != "200" {
  556. proxyConn.Close()
  557. return nil, fmt.Errorf("CONNECT failed: %s", strings.TrimSpace(statusLine))
  558. }
  559. // Consume remaining headers until empty line
  560. for {
  561. line, err := br.ReadString('\n')
  562. if err != nil {
  563. proxyConn.Close()
  564. return nil, fmt.Errorf("read CONNECT headers: %w", err)
  565. }
  566. if strings.TrimSpace(line) == "" {
  567. break
  568. }
  569. }
  570. // Clear deadline — gotd manages its own timeouts
  571. proxyConn.SetDeadline(time.Time{})
  572. return proxyConn, nil
  573. }, nil
  574. default:
  575. return nil, fmt.Errorf("unsupported proxy scheme: %s", u.Scheme)
  576. }
  577. }
  578. // encodeBasicAuth returns base64-encoded "user:password" for proxy auth.
  579. func encodeBasicAuth(user, password string) string {
  580. return base64.StdEncoding.EncodeToString([]byte(user + ":" + password))
  581. }
  582. // resolveInputPeer resolves a username to an InputPeer
  583. func (c *Client) resolveInputPeer(ctx context.Context, api *tg.Client, username string) (tg.InputPeerClass, error) {
  584. resolved, err := api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
  585. Username: username,
  586. })
  587. if err != nil {
  588. return nil, wrapFloodWait(err)
  589. }
  590. switch p := resolved.Peer.(type) {
  591. case *tg.PeerChannel:
  592. for _, ch := range resolved.Chats {
  593. if channel, ok := ch.(*tg.Channel); ok && channel.GetID() == p.ChannelID {
  594. accessHash, _ := channel.GetAccessHash()
  595. return &tg.InputPeerChannel{
  596. ChannelID: p.ChannelID,
  597. AccessHash: accessHash,
  598. }, nil
  599. }
  600. }
  601. return &tg.InputPeerChannel{ChannelID: p.ChannelID}, nil
  602. case *tg.PeerUser:
  603. for _, u := range resolved.Users {
  604. if user, ok := u.(*tg.User); ok && user.GetID() == p.UserID {
  605. accessHash, _ := user.GetAccessHash()
  606. return &tg.InputPeerUser{
  607. UserID: p.UserID,
  608. AccessHash: accessHash,
  609. }, nil
  610. }
  611. }
  612. return &tg.InputPeerUser{UserID: p.UserID}, nil
  613. case *tg.PeerChat:
  614. return &tg.InputPeerChat{ChatID: p.ChatID}, nil
  615. }
  616. return &tg.InputPeerEmpty{}, nil
  617. }
  618. // extractMessages extracts messages from a MessagesMessagesClass
  619. func extractMessages(result tg.MessagesMessagesClass) []Message {
  620. var rawMsgs []tg.MessageClass
  621. switch v := result.(type) {
  622. case *tg.MessagesMessages:
  623. rawMsgs = v.Messages
  624. case *tg.MessagesMessagesSlice:
  625. rawMsgs = v.Messages
  626. case *tg.MessagesChannelMessages:
  627. rawMsgs = v.Messages
  628. case *tg.MessagesMessagesNotModified:
  629. return nil
  630. }
  631. var msgs []Message
  632. for _, raw := range rawMsgs {
  633. switch m := raw.(type) {
  634. case *tg.Message:
  635. msg := Message{
  636. ID: m.GetID(),
  637. Text: m.GetMessage(),
  638. IsService: false,
  639. }
  640. // Extract forward source channel username
  641. if fwd, ok := m.GetFwdFrom(); ok {
  642. if fromID, ok := fwd.GetFromID(); ok {
  643. if peerCh, ok := fromID.(*tg.PeerChannel); ok {
  644. _ = peerCh // We'd need channel map to resolve username; skip for now
  645. }
  646. }
  647. }
  648. // Extract t.me links from text
  649. msg.Links = tmeRegexp.FindAllString(msg.Text, -1)
  650. msgs = append(msgs, msg)
  651. case *tg.MessageService:
  652. msgs = append(msgs, Message{
  653. ID: m.GetID(),
  654. IsService: true,
  655. })
  656. }
  657. }
  658. // Sort by ID ascending
  659. sort.Slice(msgs, func(i, j int) bool {
  660. return msgs[i].ID < msgs[j].ID
  661. })
  662. return msgs
  663. }
  664. // isFloodWait 检查错误是否是 FloodWait,提取等待时间
  665. func isFloodWait(err error) (bool, int) {
  666. if d, ok := tgerr.AsFloodWait(err); ok {
  667. return true, int(d.Seconds())
  668. }
  669. return false, 0
  670. }
  671. // wrapFloodWait wraps a FloodWait error into FloodWaitError
  672. func wrapFloodWait(err error) error {
  673. if ok, secs := isFloodWait(err); ok {
  674. return &FloodWaitError{Seconds: secs}
  675. }
  676. return err
  677. }