|
@@ -372,6 +372,61 @@ func (c *Client) ResolveGroupChannel(ctx context.Context, username string) (*tg.
|
|
|
return nil, nil, fmt.Errorf("无法解析群组为超级群组: %s", username)
|
|
return nil, nil, fmt.Errorf("无法解析群组为超级群组: %s", username)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// JoinChannel makes the current account a member of the given channel/supergroup.
|
|
|
|
|
+// USER_ALREADY_PARTICIPANT is treated as success. FloodWait is wrapped normally.
|
|
|
|
|
+// Side effect: this account becomes visibly a member of the group — make sure
|
|
|
|
|
+// the caller actually wants that (private groups require it to see the member
|
|
|
|
|
+// list, but it leaves a trace in group join/leave activity logs).
|
|
|
|
|
+func (c *Client) JoinChannel(ctx context.Context, ch *tg.InputChannel) error {
|
|
|
|
|
+ api, err := c.waitReady(ctx)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ _, err = api.ChannelsJoinChannel(ctx, ch)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ if tgerr.Is(err, "USER_ALREADY_PARTICIPANT") {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return wrapFloodWait(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GetChatParticipantsByID fetches members of a basic (non-supergroup) chat.
|
|
|
|
|
+// Basic chats have no pagination — this returns everyone in one call.
|
|
|
|
|
+func (c *Client) GetChatParticipantsByID(ctx context.Context, chatID int64) ([]GroupParticipant, error) {
|
|
|
|
|
+ api, err := c.waitReady(ctx)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return c.getChatParticipants(ctx, api, chatID)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ResolveGroupPeer is a broader resolver than ResolveGroupChannel: it also
|
|
|
|
|
+// returns a basic-chat ID when the target is not a supergroup/channel.
|
|
|
|
|
+// Exactly one of (inputCh, chatID) will be non-zero on success.
|
|
|
|
|
+func (c *Client) ResolveGroupPeer(ctx context.Context, username string) (*tg.InputChannel, *tg.Channel, int64, error) {
|
|
|
|
|
+ api, err := c.waitReady(ctx)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, nil, 0, err
|
|
|
|
|
+ }
|
|
|
|
|
+ username = strings.TrimPrefix(username, "@")
|
|
|
|
|
+ resolved, err := api.ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{Username: username})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, nil, 0, wrapFloodWait(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, ch := range resolved.Chats {
|
|
|
|
|
+ if v, ok := ch.(*tg.Channel); ok {
|
|
|
|
|
+ accessHash, _ := v.GetAccessHash()
|
|
|
|
|
+ return &tg.InputChannel{ChannelID: v.GetID(), AccessHash: accessHash}, v, 0, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if p, ok := resolved.Peer.(*tg.PeerChat); ok {
|
|
|
|
|
+ return nil, nil, p.ChatID, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil, nil, 0, fmt.Errorf("无法解析群组: %s", username)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// FetchParticipantsByQuery runs ChannelParticipantsSearch for one query string,
|
|
// FetchParticipantsByQuery runs ChannelParticipantsSearch for one query string,
|
|
|
// paginating through all pages. Returns the users surfaced by this query and
|
|
// paginating through all pages. Returns the users surfaced by this query and
|
|
|
// the total count reported by TG. On FloodWait, returns a *FloodWaitError.
|
|
// the total count reported by TG. On FloodWait, returns a *FloodWaitError.
|