少し前に、私はTelegramボットAPIに基づいたテキストベースのマルチプレイヤー戦略を開発するために座って、1か月後、私はわずかながらプレイ可能な機能を備えた最初のリリースを開始しました。 知人のチェーンによると、ゲーム内の紹介プログラムのおかげで、ゲームはすぐに小さなアクティブな視聴者を獲得し、翌日も獲得し続けました。 そして、毎日のオンラインが200人のユーザーのマークを超えるまで、すべてがうまくいくようです。 これが問題の始まりです。 ユーザーは、ボットが数分間応答しない理由を尋ねるようになりました。 プレイヤーへの不快感は、特に戦争中、ユーザーが反撃のために軍隊を迅速に回復させようとし、ゲームが裏切ってハングし、どのアクションにも応答しなかったときに、最も強くなりました。 さらに、Telegramはすべてのメッセージの送信と、特定のコンテンツのメッセージのみを頻繁に使用することを禁止できます。たとえば、標準的な量的価値を持つカスタムキーボードボタンが使用されるリソースの購入や戦士の採用などです。
ただし、ボットAPIの使用経験は少なく、送信強度は低くなっています。 また、制限についても知られていましたが、実際にそれらを見つけたのは、グループで作業しているときだけでした。 パーソナルチャットを使用する場合よりも、すべてが非常に困難です。 制限の詳細については、公式のTelegram WebサイトのFAQを参照してください。
ボットが制限に達しています。これを回避するにはどうすればよいですか?
特定のチャット内でメッセージを送信するときは、1秒間に複数のメッセージを送信しないでください。 この制限を超える短いバーストを許可する場合がありますが、最終的には429エラーの受信を開始します。
複数のユーザーに一括通知を送信している場合、APIは1秒あたり30個以上のメッセージを許可しません。 最良の結果を得るには、8〜12時間の大きな間隔で通知を分散することを検討してください。
複数のユーザーに一括通知を送信している場合、APIは1秒あたり30個以上のメッセージを許可しません。 最良の結果を得るには、8〜12時間の大きな間隔で通知を分散することを検討してください。
上記から、特定のユーザーにメッセージを1秒間に1回以上送信することは不可能であり、異なるユーザーに大量にメールを送信するときは1秒間に30を超えるメッセージを送信することはできません。 ただし、いくつかのエラーは許可されます。 したがって、1/30秒ごとにユーザーにメッセージを送信し、現在の1秒以内にユーザーにメッセージを送信したかどうかを確認する必要があります。そうでなければ、同じテストに合格した場合は次のユーザーにメッセージを送信します。
開発は最初はGo言語で行われたため、チャネルとコルーチン(ゴルーチン)があり、保留中のメッセージを送信するという考えがすぐに思い浮かびました。 まず、処理された応答をチャネルに追加し、別のストリームで、許可された1/30秒ごとにこのチャネルをレーキします。 しかし、すべてのメッセージに1つのチャネルを使用するというアイデアはうまくいきませんでした。 チャンネルからメッセージを取得し、このユーザーにまだメッセージを送信できないことを確認したら、このメッセージをどこかで取得する必要があります。 同じチャンネルに再度送信するのはよくありません。ユーザーのメッセージの時系列順を崩し、多数のアクティブなプレーヤーでこのメッセージの配信を大幅に遅延させるためです。 私の知る限り、メッセージをチャンネルから出さずに確認して次のメッセージに進むことはできません。
次に、ユーザーをチャンネルに紹介するというアイデアが表示されます。 この瞬間からより詳細に。
// , id var deferredMessages = make(map[int]chan deferredMessage) // var lastMessageTimes = make(map[int]int64) // chatId – id , // method, params, photo – bot API Telegram // callback API type deferredMessage struct { chatId int method string params map[string]string photo string callback func (SendError) } // func MakeRequestDeferred(chatId int, method string, params map[string]string, photo string, callback func (SendError)) { dm := deferredMessage{ chatId: chatId, method: method, params: params, photo: photo, callback: callback, } if _, ok := deferredMessages[chatId]; !ok { deferredMessages[chatId] = make(chan deferredMessage, 1000) } deferredMessages[chatId] <- dm } // error.go, ChatId – id type SendError struct { ChatId int Msg string } // error func (e *SendError) Error() string { return e.Msg }
今、外出先で、選択されたケースデザインを使用して、結果のチャネルセットを処理したいのですが、問題は各ケースの固定チャネルセットを記述することであり、私たちの場合、ゲーム中にユーザーが追加され、ユーザーの新しいチャネルを作成するため、チャネルセットは動的ですメッセージ。 それ以外の場合は、ロックなしで行うことはできません。 それから、いつものようにGoogleに目を向けると、StackOverflowの広大さに優れたソリューションがありました。 そして、 リフレクトパッケージから選択機能を使用することにあります。
要するに、この関数を使用すると、事前に形成されたSelectCaseの配列から抽出することができます。各配列には、チャネル、送信準備ができたメッセージが含まれています。 原則は、選択した場合と同じですが、未定義のチャネル数があります。 それが必要です。
func (c *Client) sendDeferredMessages() { // 1/30 timer := time.NewTicker(time.Second / 30) for range timer.C { // SelectCase' , cases := []reflect.SelectCase{} for userId, ch := range deferredMessages { if userCanReceiveMessage(userId) && len(ch) > 0 { // case cs := reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)} cases = append(cases, cs) } } if len(cases) > 0 { // _, value, ok := reflect.Select(cases) if ok { dm := value.Interface().(deferredMessage) // API _, err := c.makeRequest(dm.method, dm.params, dm.photo) if err != nil { dm.callback(SendError{ChatId: dm.chatId, Msg: err.Error()}) } // . lastMessageTimes[dm.chatId] = time.Now().UnixNano() } } } } // func userCanReceiveMessage(userId int) bool { t, ok := lastMessageTimes[userId] return !ok || t + int64(time.Second) <= time.Now().UnixNano() }
順番に。
- まず、必要な1/30秒ごとに「チェック」するタイマーを作成し、そのループを開始します。
- その後、必要なSelectCaseの配列の形成を開始し、マップチャネルを並べ替え、ユーザーが既にメッセージを受信できる空でないチャネルのみを配列に追加します。つまり、最後の送信から1秒が経過しました。
- 2つのフィールドを埋める必要がある各チャネルのreflect.SelectCase構造を作成します:Dir-方向(チャネルへの送信またはチャネルからの抽出)、この場合はReflect.SelectRecv(抽出)フラグを設定し、Chan-チャネル自体を設定します。
- SelectCaseの配列の形成が完了したら、それをReflect.Select()に渡し、SelectCase配列のチャネルID、チャネルから抽出された値、および成功した操作のフラグを取得します。 すべて順調であれば、APIリクエストを作成してレスポンスを取得します。 エラーを受け取ったら、コールバックを呼び出してエラーを渡します。 最後に送信されたメッセージの日付をユーザーに書き込むことを忘れないでください
だから、すべてがシンプルに思えます。 これで、Telegramはユーザーに頻繁にメッセージを送信するためボットを選択せず、プレイヤーは快適にプレイできます。 もちろん、膨大な数のユーザーの場合、メッセージはますますゆっくりとゆっくりとプレーヤーに送信されますが、これは均等に行われるため、制限に従わない場合は数分間の単一ロックよりも不便さが少なくなります。
ところで、FAQで指定されているエラーを思い出します。 私の実装では、1/30秒に1回ではなく、1秒に2メッセージをユーザーに送信しますが、推奨されるよりもはるかに頻繁に1/40に1回送信します。 しかし、これまでのところ問題は発生していません。
クライアントのソースコードはgitlabで表示できます
まあ、誰かがそれが何であるかに興味を持っていたら、Telegram @ BastionSiegeBotで