خب سلام دوستان .
در این قسمت میخوایم رباتمون رو کامل کنیم، یه قسمت برای فرستادن پیام ها به کاربران و همچنین ذخیره کردن id کاربر داخل دیتابیس و یه قسمت برای فوروارد کردن پیام به کاربران و یه قسمت هم برای جوین دادن ربات داخل لینک یا لینک هایی که ادمین میفرسته(لینک گروه) رو به ربات اضافه کنیم .
در اخر این پست لینک گیت هاب هستش میتونید برید کد هایی که نوشتیم رو ببینید و...
ما تقریبا همیشه میام یه ربات api واسط میسازیم و با اون ربات میایم ربات های کلاینتمون رو کنترل میکنیم و عملی که میخوایم رو انجام میدیم چون یک سری قابلیت ها دارن که کار کردن با ربات رو ساده تر میکنه و خیلی بهتر هستش مثلا برای رباتی که نوشتیم زمانی که جمله های ربات زیاد بشه ما باید یه پیجینیشنی داشته باشیم که مثلا تو هر صفحه 10 تا پیام نشون بده بقیش تو صفحه های بعدی نشون بده مثلا دکمه next و back داشته باشه کاربر بزنه روشون و عقب و جلو کنه داخل پیام ها و همچین مواردی.. شاید بعدا یه اموزش گذاشتیم برای وصل کردن این دوتا بهم و کار کردن باهاشون...
خب برای این قسمت ما میایم یه فایل جدید به اسم user_section یا هر اسمی که میخواید داخل دایرکتوری plugins ایجاد میکنیم و کد هایی که قرار مروبط به تعامل ربات با کاربر باشه رو اینجا مینویسیم .
خب در کد بالا اومدیم مثل همیشه هندلر مون رو اول تعریف کردیم اما این دفعه اومدیم 2 تا یوزر ادمین و SpamBot رو not کردیم و group رو هم 3 قرار دادیم چون 2 تا از قبل تعریف شده .
در کد بالا ما هرکسی که پیام بده ربات این تابع اجرا میشه ولی ما نمیخوایم برای ادمین و اسپم بات(اسپم بات به این دلیل که وقتی ما کامند start رو تو قسمت status میفرستیم اون یه جوابی میده، زمانی که جواب میده در واقع پیام فرستاده و وارد این تابع میشه) این قسمت اجرا بشه برای همین not کردیم این دو یوزر رو.
خب بریم ببینیم چی نوشتیم:
قسمت اول مثل همیشه چت ایدی رو ذخیره کردیم بعد شرط گذاشتیم ببینیم اصلا پیامی داخل دیتابیس هست یا نیست و بعد اگر پیامی بود مثل قسمت قبلی جمله ها رو از دیتابیس گرفتیم و داخل متغیر data ذخیره کردیم در ادامه یه حلقه زدیم رو data و درون حلقه برای هر پیام یه چت اکشن فرستادیم بعد جمله رو فرستادیم و برای هر جمله هم 3 ثانیه اسلیپ دادیم یعنی پیام اول میره 3 ثانیه صبر میکنه جمله بعدی...
اینجا ما داخل دیتابیس اومده بودیم قسمت اول جمله یه عددی رو مشخص کرده بودیم و ازش برای حذف کردن جمله ها استفاده کردیم ولی اینجا برای کاربر نباید اون رو بفرستیم برای همین میایم اون قسمت اولش رو نادیده میگیریم به این شکل:
message.split(":", 1)[1]
الان اینجا درون حلقه هربار یکی از جمله ها رو میگیره و ما میایم 1 بار اسپلیتش میکنیم با : و بعد الان وقتی جدا میشه همچین دیتایی خواهیم داشت
["1", "test-0"]
خب اینجا قسمت اول میشه عدد مربوط به هرجمله که نباید برای کاربر ارسال بشه پس میایم ایندکس دوم لیستمون رو میفرستیم برای کاربر.
اگر هم پیامی داخل دیتابیس نبود میایم برای کاربر یه سلام میفرستیم..
اما دو خط اخر میایم داخل دیتابیس ایدی کاربر رو ذخیره میکنیم :
current_user = await c.get_me() r.sadd(f"users:{current_user.id}", m.from_user.id)
اینجا ما از یه متود جدید به اسم get_me استفاده کردیم، این متود میاد اطلاعات این اکانتی که الان باهاش به تلگرام وصل شدیم رو بهمون میده( در واقع تایپ User رو بهمون برمیگردونه ) و بعد ما اومدیم به دیتابیس ایدی این یوزری که پیام فرستاده به ربات رو داخل دیتابیس ذخیره کردیم اینجا ما کلید رو برابر
users:{current_user.id}
قرار دادیم ، میتونستید کلا این قسمت current_user رو بردارید ننویسید به این شکل:
r.sadd("users", m.from_user.id)
ولی من current_user رو هم ایجاد کردم برای اینکه مثلا اگر چند اکانت استفاده کردید تعداد یوزر های هر اکانت جدا باشه.
نکته: تو کدی که نوشتیم هربار که کاربر پیام بده به ربات این تابع اجرا میشه و پیام ها برای کاربر ارسال میشن که این زیاد جالب نیست برای همین میتونید یک مقداری رو داخل دیتابیس بعد از فرستادن پیام ذخیره کنید و قبل از اینکه پیامی رو بفرستید به کاربر بیاید مقدارش روچک کنید اگر نبود بعد بیاید پیام رو بفرستید به کاربر(اینجوری اگه از قبل پیام داده باشه بهش دیگه نمیده) حالت های دیگه ای رو هم میتونید خودتون بنویسید...
خب ما چون در بالا اومدیم یوزر ها رو داخل دیتابیس ذخیره کردیم اینجا میایم فایل info_section مون رو هم تغیر میدیم و به این شکل تعداد یوزر های موجود رو میگیریم.
اینجا از متود scard ردیس استفاده کردیم، این متود میاد تعداد ولیو هایی که برای اون کلیدمون داخل set ذخیره کرده بودیم رو برمیگردونه؛ دقت کنید که این متود برای set هستش بالاتر که sadd کردیم داخل یک set اون یوزر ایدی ها رو ذخیره میکنه .
خب الان میخوایم یه پیامی رو به یه تعداد خاصی از کاربر های ربات یا همه کاربر ها ارسال کنیم اینجا گفتیم برای اینکه به یه تعداد خاص از کاربر ارسال کنید یه عدد رو بفرستید و اگر میخواید به همه کاربر ها ارسال کنید * رو بفرستید.
تو کد بالا اومدیم مثل همیشه هندلر مون رو تعریف کردیم و کامند forward رو هم فیلتر کردیم بعد اومدیم پیامی که میخوایم فرستاده بشه رو نوشتیم و در ادامه یه چت اکشن و بعدش پیام رو فرستادیم در اخر هم داخل دیتابیس مثل همیشه که میخوایم input بگیریم step رو مشخص کردیم مثلا اینجا offset کلید forward msg رو برابر 0 و valueش رو هم برابر 1 قرار دادیم برای step اول که گرفتن تعداد کاربر هستش.
و اومدیم offset استپ بعدی رو 1 قرار دادیم و مقدارش رو گذاشتیم 0 ، این step برای گرفتن پیامی هستش که میخوایم فوروارد کنیم اگر اینجا مقداری براش تایین نکنیم و تو مراحل بعدی وسطش کاربر بیاد کامند forward رو بفرسته اون رو به عنوان پیام ممکنه ارسال بکنه چون اونجا مثلا step چیز دیگه ای بود پس بهتر چنین جا هایی شما step رو 0 بکنید داخل این مرحله اولیه که به اشتباه وارد اون هندلر هایی که نوشتید نشه.
نکته: اینجا درسته که کلید یکی هستش یعنی هردو forward:msg هست ولی تو متود setbit میتونید تعداد زیادی offset داشته باشید اینجا برای استپ اول افست رو 0 قرار دادیم و برای استپ بعدی افست رو 1 دادیم
اینجا میخوایم تعدادی که کاربر میفرسته رو بگیریم و یه ولیدیتی انجام بدیم و همچنین step رو هم تغیر بدیم
خب مثل قبل هندلر مون رو نوشتیم و group رو 4 گذاشتیم چون قبلا 3 تا group تعریف کردیم.
در ادامه یه شرط نوشتیم که اگر مقدار forward:msg برابر 1 بود و همچنین msg هم وجود داشت؛ در اینجا ممکن کاربر فایل یا اهنگ و.. بفرسته که اینها text نداشته باشه در اینصورت ارور میگیریم چون and بعدی گفتیم اگر با / شروع نشده بود بیا وارد شرط بشو پس برای همین اینجا and msg رو هم مینویسیم.
بعد داخل شرط اول یه try نوشتیم چون ما گفتیم به کاربر بیا یه عدد بفرست یا * ولی اگه کاربر چیز دیگه ای فرستاد چی؟ باید این رو هم هندل کنیم خب ما اومدیم گتفیم اگر مسیج برابر بود با * یا تونستی اون مسیج رو تبدیل کنی به int برو داخل شرط اگر هم نتونستی int بکنی یعنی پیام کاربر متنی یا چیز دیگه ای بوده و الان بهمون ValueError ارور میده که داخل except گفتیم اگر ValueError بود بیا به کاربر بگو فرمت درست رو ارسال بکنه .
بعد از اینکه وارد شرط شدیم اومدیم یه چت اکشن فرستادیم و بعدش یه پیام فرستادیم به کاربر که بیاد پیامی که میخواد ارسال بشه رو بفرسته در ادامه اومدیم یه مقادیری رو داخل دیتابیس ست کردیم اینجا اولین مقدار تعداد یوزر هایی هستش که کاربر میخواد بهشون پیام بفرسته، در خط بعدی اومدیم ایدی این پیامی که الان فرستادیم به ادمین رو هم ذخیره کردیم (در ادامه بهش نیاز داریم) و 2 خط بعدی هم step رو مشخص کردیم اول step قبلیمون رو 0 کردیم که وارد اینجا نشه و بعد استپ جدیدی رو 1 کردیم برای گرفتن پیامی که میخواد ارسال بکنه.
الان ما تعداد کاربرانی که پیام قرار براشون ارسال بشه رو داریم فقط کافیه پیامی که قرار ارسال بشه رو از ادمین بگیریم و ارسال کنیم برای اینکار میایم یه هندلر دیگه مینویسیم مثل قبلی که نوشتیم:
خب در اینجا دقیقا مثل قبل هندلر مون رو تعریف کردیم فقط group رو یکی زیاد کردیم و بعد یک شرط نوشتیم که اگر step مون اونی بود که تو مرحله قبل نوشتیم بیا برو داخل شرط: بعد داخل شرط میایم ایدی پیامی که قبلا ذخیره کردیم رو از دیتابیس میگیریم بعد یه شرط میزاریم که اگر ایدی این پیامی که الان فرستاده شده برابر بود با اون ایدیی که تو مرحله قبل داخل دیتابیس ذخیره کردیم بیا برو داخل شرط :
نکته : تو مرحله قبل اگر ایدی پیامی که فرستادیم رو ذخیره نمیکردیم و الان هم چک نمیکردیم یه مشکل بوجود میومد زمانی که پیام رو میفرستیم این هندلر هایی که نوشتیم group شون فرق میکه پس اون پیامی که میاد رو دریافت میکنن اینجا ما میایم شرط میزاریم و مراحل رو جدا میکنیم ولی اینجا وقتی میگه به کاربر عدد یا * رو بفرست اگه کاربر مثلا یه عدد بفرسته بعدش میاد وارد forward_msg_S2 میشه و اونجا step تغیر میکنه و وقتی وارد هندلر دیگه میشه (هردو هندلر پیام رو گرفتن اول اونی که اولویتش پایین تره زود تر اجرا میشه بعد اونی که اولویتش بیشتر، الان اینجا forward_msg_S2 اولویتش کمتر بود پس اون اول گرفت و step رو هم تغیر داد والان وقتی وارد forward_msg_S3 میشه اونجا شرط ما که دیتابیس چک میکنیم درست هستش و اونجا خود به خود اون چیزی که قرار بود عدد ما باشه میشه پیام ما و همون برای کاربر ارسال میشه برای حل این مشکل میتونید یا به اینشکلی که نوشتیم مسیج ایدی رو هم چک بکنید یا اینکه بیاید group رو تغیر بدید یعنی مثلا برای forward_msg_S3 بیاید groupرو بزارید 4 و برای forward_msg_S2 بزارید 5 اینجوری اول این اجرا میشه شرط درست نیست بیخیال میشه میره اون یکی رو چک میکنه شرط درست هستش و ادامه ماجرا...
تو شرط میایم یه counter تعریف میکنیم برای اینکه تو مرحله قبل اگر کاربر * نداده بود برای تعداد ارسال پیام و عدد داده بود اینجا بشماریم و وقتی به اون تعداد رسید دیگه پیام نفرستیم .
بعدش میایم مثل قبل از متود get_me استفاده میکنیم و ایدی اکانت رو میگریم و یک حلقه میزنیم رو دیتابیس و یوزر id ها رو میگیریم بعدش داخل حلقه یه چت اکشن ارسال میکینم و بعد از متود forward_messages استفاده میکینم این متود فوروارد میکنه پیام رو چون ببینید❌ پیامی که ادمین ست میکنه برای راسال ممکنه کپشن داشته باشه عکس باشه یا فیلم یا هرچیز دیگه ای برای همین ما ساخته بیایم خودمون دونه دونه جدا کنیم داخل دیتابیس ذخیره کنیم بعد اینجا باز بهم وصلشون کنیم بعد تازه بیایم ارسال بکنیم میبینید که کار اشتباهی هستش❌ پس مایم id اون پیامی که ادمین فرستاده رو میگیریم و فورواردش میکنیم اگر نمیخواید وقتی که به کاربر ارسال میشه بنویسه فوروارد شده این پیام از طرف x میتونید از متود copy_messages استفاده کنید.
بعد نوشتیم که اگر تعدادی که ادمین مشخص کرده تو مرحله قبل * نبود و همچنین کانتر ما برابر اون شده بود بیا اینجا break کن دیگه پیام نفرست اینجوری به اون تعداد که ادمین گفته پیام میفرستیم(پیشنهاد میکنم اینجا یه اسلیپ چند ثانیه ای اعمال کنید چون اگر تعداد زیاد باشه و شما پشت سر هم زیاد پیام ارسال کنید به ارور flood wait برمیخورید که در ادامه باز بیشتر میگیم راجب این ارور )
خب بیایم یکم بیشتر راجب متود forward_messages بگیم چون چیزی راجب ارگمان هاش و.. نگفتیم:
و اما متود فورواردی که استفاده کردیم پارامتر هاش به چه شکل هست:
chat_id (int
|str
)
chat_id (int
|str
)
message_ids (int
| List ofint
)
disable_notification(bool
)
schedule_date(int
)
پارامتر اول ایدی فردی هستش که میخوایم فوروارد بشه براش ( مقصد )
پارامتر دوم چت ایدی مبدا هستش در اینجا ما اومدیم همین چتی که با ربات داشتیم رو دادیم بهش چون میخواستیم یپامی که اینجا هست رو برای کاربر فوروارد کنیم .
پارامتر بعدی id اون پیامی هستش که قرار فوروارد بشه که میتونه لیستی از پیام ها یا فقط یک پیام باشه.
پارامتر بعدی هم میتونید ست کنید که نوتیفای بره یا نره و پارمتر اخر هم یه Unix time رو میتونید ست کنید که اون زمان پیام فوروارد بشه.
قبلا پارامتر های دیگه ای رو میگرفت متود فوروارد مثلا as_copy ولی تو اپدیت های جدید مثلا متود
copy_message رو اضافه کردن که بهتون اجازه میده خیلی چیزا رو روی پیام ست کنید .
اینجا میخوایم یه پیامی از کاربر بگیریم که بعد از اینکه وارد گروه شد اون رو ارسال بکنه:
در کد بالا اول از همه اومدیم errors رو هم ایمپورت کردیم برای هندل کردن ارور ها که در ادامه بهش میرسیم...
مثل قبل هندلر مون رو نوشتیم یه چت اکش ارسال کردیم و step مون رو هم مشخص کردیم ولی اینجا چت اکشن برای اینکه هربار اون تایم رو ننویسیم اومیدم یه تابع اینشکلی اینجاد کردیم هرجا نیاز شد صداش میزنیم(درواقع یه کوروتین نوشتیم که هرموقع نیاز شد یه تسک ایجاد میکنیم و await میکنم تسکمون رو (قسمت قبل یه لینک راجب کوروتین گذاشته بودم حتما ببینید))
خب باز مثل قبل هندلر مون رو نوشتیم و بعد group رو هم برابر 6 قرار دادیم چون قبلا 5 تا استفاده کردیم و بعد ما شرط گذاشتیم که ببینم در step درستی هستیم یا نه و همچنین msg تکست وجود داره و همچنین با / شروع نشده باشه اگه این شرایط برقرار بود بیاد یه چت اکشنی بفرسته وهمچنین اون پیام رو داخل دیتابیس ذخیره بکنه و به کاربر بگه ذخیره شد اخرش هم داخل دیتابیس stepرو 0 میکنیم.
خب در کد بالا ما میخوایم لینک گروه رو از کاربر بگیریم اینجا میایم مثل قبل هندلر مون رو مینویسیم و بعد یه چت اکشن ارسال میکنیم و همچنین یه پیامی میفرستیم که بیاد لینک گروه رو بفرسته در اخر هم step رو برابر 1 قرار میدیم.
خب باز هم هندلر مون رو نوشتیم و یکی به group اضافه کردیم و بعد چک کردیم ببینیم step درست هستش یا نه و همچنینی msg.text وجود داره یا نه و با / شروع نشده باشه.
در ادامه یه چت اکشن نوشتیم و بعد یه try گذاشتیم که ارور ها رو هندل کنیم اگه یادتون باشه تو این فایل ما اول اومدیم errors روهم ایمپورت کردیم ما اینجا ازش برای هندل کردن ارور های احتمالی استفاده میکنیم.
اول چک میکنیم ببینم لینکی که کاربر فرستاده برای گروه پابلیک هست یا پرایویت گروه های پرایویت داخل لینکشون joinchat هستش برای همین چک میکنیم اگر داخل لینک کاربر بود پس پرایویت چت هستش و مستقیم با متود join_chat وارد اون گروه میشیم .
متود join_chat پارامتر های زیادی رو نمیگیره فقط لینک گروه یا چنل رو میگیره و یا ایدی عددی اون گروه یا چنل رو .
اگه دقت کنید من اومدم چیزی که متود join_chat برمیگردونه رو ریختم داخل متغیر resp این رو در ادامه باهاش کارداریم ، چیزی هم که برمیگردونه خود Chat هستش که در این لینک میتونید ببینید که Chat دقیقا چه پارامتر هایی داره و همینا رو هم برمیگردونه.
در ادامه اومدیم یه پیام فرستادیم برای ادمین که با موفقیت وارد گروه شدیم و بعدش هم یک شرط گذاشتیم که اگر ولیویی برای join_msg داخل دیتابیس وجود داشت بیاد یه چت اکشن بفرسته و اون پیام رو هم به اون گروه بفرسته. دقت کنید بالاتر زدیم پیام رو بفرست به chat_id که متغیری هستش که بالا تر تعریفش کردیم و مقدارش میشه چت ایدی همین جایی که با ادمین تعامل داره ربات ولی اینجا اومدیم از اون resp استفاده کردیم چون میخوایم چت اکشن رو در اون گروه ارسال کنیم نه در pv به همین دلیل بود که مقدار return شده join_chat رو ریختیم داخل resp.
خب کدی که بالا نوشتیم ممکنه ارور هایی رو دریافت کنه این ارور ها رو ما باید هندل کنیم برای هندل کردن اررو ها شما از لایبری پایروگرام همونطور که اول این فایل نوشتیم میاید errors رو هم ایمپورت میکنید و بعد داخل except ها میاید میگید اگه اون ارور بود بیاد فلان رفتار رو انجام بده مثلا اینجا ما اولی except که نوشتیم برای هندل کردن ارور این بوده که کاربر قبلا داخل گروه بوده یا اینکه لینک اشتباه فرستاده، لینک اصلا وجود نداره و .... مواردی که بیشتر بهش میخورید و در ذهنم بود رو هندل کردم که به ادمین بگیم دقیقا چه اتفاقی افتاده مثلا ارور اخری میگیم که اونقدر تایم باید صبر کنه تا اینکه بتونه وارد اون گروه بشه ربات چون محدود شده و اگر اروری رو هم در اینجا ننوشتم و بهش برخوردید باز میتونید به همین شکل هندل کنید .
البته ارور اخر که یه تایمی رو باید صبر کنه معمولا میایم از اسلیپ ماژول asyncio استفاده میکنیم برای اینکه به اون مدت گفته شده sleep بدیم بهش.
در اخر اومدیم داخل دیتابیس Step مون رو هم 0 کردیم همه چی اوکی باشه.
خب همونطور که میبیند اوکی بود و پیام رو هم ارسال کرد داخل اون گروه مشکلی نبود اما برای اون ارور هایی که هندل کردیم هم خروجیمون اینشکلی میشه:
خب ارور هایی هم که هندل کردیم همچین شکلی خواهند داشت باز اگه به ارور دیگه برخورد کردید میتونید خیلی راحت هندل کنید و برید جلو ...
نکته: تا اینجای کار ما Step هایی که هندل کردیم درست کار میکنن ولی یه مشکلی هست زمانی که مثلا بزنید join_chat/ بهتون میگه لینک رو بفرستید اینجا مثلا یکی بیاد به جای لینک بزنه join_msg/ ازش میخواد که پیام رو بفرسته وخب اگر بفرسته باز میاد اون رو ذخیره میکنه و همچنین ارور میده که لینک معتبر نیست اگر خواستید این موارد رو هم هندل کنید میتونید یا بیاید اینجا تو هر مرحله تمامی step ها رو setbit کنید یعنی هر قسمت فقط step مربوط به خودش 1 بشه و بقیه رو 0 بکنید ولی اینجوری یکم طولانی میشه اگه دارید از ردیس استفاده میکنید میتونید از متود hset استفاده کنید به این شکل که یه کلید میزارید مثلا Bot:Steps بعد در هر قسمت کل استپ ها رو میدید بهش مثلا
r.hset("Bot:Steps", mapping={"join:chat:link":0, "join:chat:msg":1})
اینشکلی میتونید چندین step رو همزمان تایین کنید و اون مشکل که گفتم پیش نماید چون بقیه step ها 0 شدن و زمانی که چک میشن دیگه اجرا نمیشن(شاید بعدا تو گیت هاب Step رو اینشکلی اضافه کنم براش).
https://redis.io/commands/hset
و اینکه میتونید این اپشن ها رو باز زیاد تر کنید بهتر کنید و یا هر چیزی که به ذهنتون میرسه مثلا این پیامی که میدیم برای ارسال رو بتونیم نمایش و تغیرش بدیم...
تا همینجا برای این رباتمون کافیه و دیگه اپشنی بهش اضافه نمیکنیم احتمالا... شاید بعدا یه ربات دیگه هم نوشتیم فعلا مطمن نیستم میتونید نظر بدید که ربات های api بنوسیم یا client یا هرچیز دیگه ای ...
و مرسی که خوندید و با من همراه بودید تو این ماجرا امید وارم راضی بوده باشید و خوشحال و خندون به زندگیتون ادامه بدید، چاکس همگی ❤️
اگر نظری انتقادی پیشنهادی چیزی داشتید میتونید کامنت بزارید ✍️
کد هایی که نوشتیم رو هم در لینک زیر میتونید مشاهد کنید :