Instagramでの大量流出のためのTelegramボットの開発





私は自分の仕事の結果、Instagramでの大量フォローのためのTelegramボットの開発を共有したいと思います。



ほとんどの場合、この用語に慣れていない人もいます。簡単な説明を次に示します。

Massfollowing-特定の基準に従って人々への大量購読。

簡単に言えば、あなたは人を購読します、彼は誰かが彼に購読していることをストリームで見て、あなたのページに行きます。



これにより、ビジネス向けのこのツールの目標が達成されます。

マスフォローのサービスはすでにかなり前に存在しており、約2年前、Instagramでの友人の活動、つまりこのソーシャルネットワークを通じてビジネスを促進したいという気持ちに気付きました。 これを行うために、彼らはさまざまな大規模なサブスクリプションサービスを使用しました。そこでは、毎月のサブスクリプションに約1000ルーブルがかかります。



それは私にとって面白くなり、人気のあるサービスの1つに登録し、機能を調べました。

TelegramがBot Platformを導入した瞬間、劇的に人気を博し、多くの開発者がそれに基づいて何かを試みました。特に、私はサイトから通知を送信するために使用しました。



そのため、サイトでの作業はかなり不便だと思いました。たとえば、サブスクリプションのタスクを開始し、1週間で終了しました。完了したことすらわからず、忘れてしまうこともありました。 また、私は常にサイトにアクセスし、ログインし、タスクを監視し、新しいタスクを作成する必要がありました。



ソリューションは簡単でした。アカ​​ウントを追加したり、サブスクリプションとサブスクライブ解除のタスクを作成したり、タスクの完了に関するメッセンジャーで直接通知を受け取ったりできるTelegramボットを作成してみませんか?



私は2017年3月に1年半のどこかでプロトタイプの開発を開始しましたが、当時はそうでしたが、今ではアナログが見つからなかったので、書くことにしました。



最初の難しさは、instagram APIへのアクセスであることが判明しました。もちろん、リクエスト後に取得できませんでした。解決策を探す必要がありました。すぐに見つかりました。親切な人がinstagram-private-apiリポジトリをサポートします。最も必要なアクション。



彼はNode.jsで開発を開始し、データストレージにMongoDBを使用しました。



書きたかった主な機能は次のとおりです。





次に、データベースのスキームを作成しました。ユーザー、アカウント、タスクのリスト、サブスクリプションのリスト、およびいいね!をずっと保存する必要があります(再サブスクライブしないように)。



識別のためにテレグラムユーザーIDを保存するスキーム:



const UserSchema = new db.mongoose.Schema({ id: { type: Number, required: [true, 'idRequired'] }, name: { type: String, required: [true, 'nameRequired'] }, date: { type: Date, default: Date.now } })
      
      





Instagramアカウントを保存するためのスキーム:



 const AccountSchema = new db.mongoose.Schema({ user: { type: Number, required: [true, 'userRequired'] }, login: { type: String, required: [true, 'loginRequired'] }, password: { type: String, required: [true, 'passwordRequired'] }, verified: { type: Boolean, default: false }, date: { type: Date, default: Date.now } })
      
      





タスクのスキームと同様に:



 const TaskSchema = new db.mongoose.Schema({ user: { type: Number, required: [true, 'userRequired'] }, login: { type: String, required: [true, 'loginRequired'] }, type: { type: String, required: [true, 'typeRequired'] }, params: { type: Object, required: [true, 'paramsRequired'] }, status: { type: String, default: 'active' }, date: { type: Date, default: Date.now }, start: { type: Number, required: [true, 'startReqiured'] } })
      
      





params



プロパティには、サブスクリプションなどの特定のタスクに固有のデータを格納します。

sourceType ソースの種類(ユーザー、ハッシュタグ、場所)
ソース ソース名(ユーザー名、#ハッシュタグ、[lat、long])
アクションフォロー サブスクリプション/サブスクリプションの総数
actionFollowDay 1日あたりのアクティビティ数
actionLikeDay 各ユーザーのいいねの数


他のスキームは、 instalator-telegramプロジェクトのリポジトリで利用できます。



次に、ユーザーからの受信コマンドを処理する必要がありましたが、その時点で問題があり、その時点で既製のソリューションを見つけられず、十分にシンプルにし、おそらく多くの人がこのアプローチを嫌い、パスのマップを作成しました:



プロジェクトマップ
 module.exports = { event: 'home', children: { ' ': { event: 'task:create', children: { '*': { event: 'task:select', children: { ' + ': { event: 'task:select:follow+like', children: { : { event: 'task:select:follow+like:user', children: { '*': { event: 'task:select:follow+like:user:select', children: { '*': { event: 'task:select:follow+like:source:action', children: { '*': { event: 'task:select:follow+like:source:actionPerDay', children: { '*': { event: 'task:select:follow+like:source:like' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'task:select:follow+like:hashtag', children: { '*': { event: 'task:select:follow+like:hashtag:find', children: { '*': { event: 'task:select:follow+like:source:action', children: { '*': { event: 'task:select:follow+like:source:actionPerDay', children: { '*': { event: 'task:select:follow+like:source:like' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } } } }, : { event: 'task:select:follow+like:source', children: { '*': { event: 'task:select:follow+like:source:select', children: { '*': { event: 'task:select:follow+like:source:action', children: { '*': { event: 'task:select:follow+like:source:actionPerDay', children: { '*': { event: 'task:select:follow+like:source:like' }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'task:select:type', children: { '*': { event: 'task:select:type:unfollow' // await: true }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, ' ': { event: 'account:add', children: { '*': { event: 'account:await', await: true }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'actions', children: { '*': { event: 'actions:account', children: { : { event: 'actions:account:update', children: { '*': { event: 'actions:account:update:one', children: { '*': { event: 'actions:account:update:two', children: { '*': { event: 'actions:account:update:three' } } } } }, : { event: 'location:back' } } }, : { event: 'actions:account:cancel' }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'account:list', children: { '*': { event: 'account:select', children: { : { event: 'account:edit', children: { '*': { event: 'account:edit:await', await: true }, : { event: 'location:back' } } }, : { event: 'account:delete', await: true }, : { event: 'location:back' } } }, ' ': { event: 'account:add', children: { '*': { event: 'account:await', await: true }, : { event: 'location:back' } } }, : { event: 'location:back' } } }, : { event: 'limit:message', children: { : { event: 'location:back' } } } } }
      
      







マップには「イベント」のオブジェクトが含まれ、 イベントプロパティにはEventEmitterイベントの名前が含まれ、 プロパティには構造に対応する子「ルート」が含まれます。



どのように機能するか、ユーザーはコマンドを送信します。たとえば、 タスクを作成し 、メッセージを受信した後、プロパティがある場合はツリーを検索し 、対応するEventEmitterイベントを呼び出し、ユーザーの現在の場所を格納する状態オブジェクトにユーザーIDを書き込みます:



 state = { 23445432: [' ', ' + '], 1345532: [''] }
      
      





ここで、ユーザーがID 23445432を使用してメッセージを送信すると、 タスク:select:follow + likeイベントが応答でトリガーされますが、対応するメッセージがマップ上にない場合はどうなりますか? または、たとえば、1日あたりのサブスクリプション数など、カードに書き込まれていないユーザーからデータを取得する必要がありますか? これを行うには、 のプロパティの1つに次のようにアスタリスクを付ける必要があります: * 、その中に呼び出す必要のあるイベントを記述します。



直接「ルーティング」自体:



 const router = msg => { //   if (msg.text) msg.text = emoji.decode(msg.text) // No user status, we give the main menu if (!state[msg.from.id]) { commandEvents.emit('/home', msg) // Adding the user to the state state[msg.from.id] = [] } else { // Go to the desired branch const findBranch = state[msg.from.id].reduce((path, item) => { // If there are no child partitions if (!path.children) { return path } else { if (path.children[item]) { return path.children[item] } else { // If there is no suitable branch, then we try to use a common branch if (path.children['*']) { return path.children['*'] } else { return path } } } }, map) // Call branch method const callBranch = branch => { const action = findBranch.children[branch] // Call action event.emit(action.event, msg, action, (value = msg.text) => { event.emit('location:next', msg, action, value) }) } // We check the existence of the method if (findBranch.children.hasOwnProperty(msg.text)) { callBranch(msg.text) } else if (findBranch.children['*']) { // If there is no suitable branch, then we try to use a common branch callBranch('*') } else { // back event.emit('location:back', msg) } } }
      
      





マップにプロパティがない場合、イベントをコールバックし 、ユーザーを1つ上のステップに戻します。



ハンドラーの配置方法について詳しく説明するのは意味がありません。これらは、データベース内の特定のデータを変更する通常のコールバック関数です。たとえば、ユーザーにアカウントのリストを送信するハンドラーを指定します。



 event.on('account:list', async (msg, action, next) => { try { const list = await Account.list(msg.from.id) if (list === null) { throw new Error(`There are no accounts for ${msg.from.id}`) } // Sending the list of accounts const elements = list.map(item => item.login) send.keyboard(msg.from.id, ' ', [ ...elements, ' ', '' ]) next && next() } catch (e) { event.emit('account:empty', msg) next && next() } })
      
      





next()メソッドは、ユーザーをマップの次のレベルに移動します。これにより、アクティブなセクションをstateに追加し、 キーボードメソッドがボタンをユーザーに送信します。



さらに、 タスクスキームには、分が記録され、タスクが作成されるstartプロパティがあります。したがって、タスクを開始する時間です。このため、現在の分で作成されたすべてのタスク、cron'a作業コードをソートします。



cron.js
 cron.schedule(conf.cron, async () => { try { const list = await task.currentList() if (list === null) throw new Error('No active assignments') for (let item of list) { const id = item._id.toString() // Missing running tasks if (activeTask.includes(id)) continue switch (item.type) { case ' + ': activeTask.push(id) actions .followLike(item) .then(res => { // Remove from list const keyActiveTask = activeTask.indexOf(id) delete activeTask[keyActiveTask] if (res.name === 'AuthenticationError') { send.message( item.user, `️    ${item.login}  ,  !` ) throw new Error(' ') } // notify the user when the task is completed if (res) { send.message( item.user, ` ${item.type}    ${item.login}` ) } }) .catch(e => console.log(e)) break case '': activeTask.push(id) actions .unFollow(item) .then(res => { // Remove from list const keyActiveTask = activeTask.indexOf(id) delete activeTask[keyActiveTask] if (res.name === 'AuthenticationError') { send.message( item.user, `️    ${item.login}  ,  !` ) throw new Error(' ') } // notify the user when the task is completed if (res) { send.message( item.user, ` ${item.type}    ${item.login}` ) } }) .catch(e => console.log(e)) break default: // Job type is not defined break } } } catch (e) { // No active assignments return e } })
      
      







Telegramを使用するには、webhookを設定します。まだ必要ない場合は、devmodeで実行できます(npm run dev)。



webookをセットアップすることをお勧めします。これにより、サーバーは常にポーリングリクエストを送信せず、設定はconf.jsonファイルにあります。



そしておそらく最後の1つはInstagramプライベートAPIを使用しているため、それを検討する意味はありません。ドキュメントはプロジェクトリポジトリの例とともに利用できます。



そして最後に、機能する機能の例:





おわりに



私はまだ問題があると言いたい、それらはInstagramがブロックし、VPSの1つのIPアドレスからより多くのアカウントを承認する許可を与えないという事実に関連している。 したがって、私の場合、複数のアカウントからログインすることができ、新しいアカウントを追加するためにアクセスはすでにブロックされていましたが、当然のことながら日の制限に違反しない限り、以前に追加したアカウントには問題はありませんでした。



もちろん、各アカウントまたは複数のアカウントにプロキシを使用すると、これらすべてを回避できますが、サービス全体を開発するという目標を設定しませんでした。便利で無料のツールを作成し、追加の開発経験とオープンソースへの貢献を得ることが重要でした。



以前にInstagramにログインしたことがないレンタル仮想サーバーのどこかにボットを置いた場合(もちろんopenvpnを使用していない場合)、オランダからInstagramに移動しようとしていることを確認する必要がありますサーバーにログインすると、Instagramにログインするとこのメッセージが表示されます。その後、アカウントを再度追加できます。



もちろん、これはサービスの完全な代替ではありませんが、インタビューしたほとんどの人が使用する基本的な方法を実装しました。



ボットの使用に注意してください! 特にあなたのために、「制限」セクションを追加しました。ここには、Instagramによるブロックを警告する情報が記載されています。



重要! パスワードは暗号化されていない形式で保存されるため、パスワードを入力しないでください!

プロジェクトリポジトリ



All Articles