نتجاوز حظر الرسائل API فكونتاكتي من خلال Python

مرحبا يا هابر. في مقالتي السابقة ، تحدثت عن إمكانية الوصول إلى طرق قسم الرسائل من خلال الوثائق ، والتي كانت كافية لتسجيل الدخول إلى موقع VK. ثم قال الكثيرون أن هذا لا يمثل تهديدًا للبيانات الشخصية للمستخدمين ، وعدم القدرة على ضخ رسائلهم هو عدم وجود منصة. أيضًا في التعليقات ، تركت رابطًا إلى مكتبة node.js ، والتي يمكنها تسجيل الدخول باستخدام تسجيل الدخول / كلمة المرور وتوفير الوصول إلى واجهة برمجة التطبيقات للرسائل ، وتظاهر بأنها تطبيق رسمي.

تنصل:


تم إنشاء المقالة وجميع التعليمات البرمجية المكتوبة للأغراض التعليمية والبحثية فقط ولم يتم استخدامها مطلقًا للأنشطة غير القانونية. لا يحثك المؤلف على تكرار أي من الإجراءات الموضحة هنا ولا يتحمل أي مسؤولية عنها.


ولكن ليس كل الناس على دراية بـ javascript و node.js ، لذلك قررت كتابة مكتبتي في بايثون ، والتي يستخدمها العديد من الأشخاص الآن ، مما يتيح لنا توفير الوظائف الكاملة لواجهة برمجة التطبيقات للرسائل من خلال "طلبات الاختبار" للوثائق. أطلب منك على الفور ألا تغضب مني في الأماكن التي سأكرر فيها جوانب "الكلام" السابق ، لأنني أريد أن أرتب هذه المقالة في شكل وثائق مستقلة.

كيفية استخدامها؟


تقع المكتبة نفسها في مستودع github-a (في نفس المكان ، في مجلد الأمثلة ، يوجد نص برمجي يحتوي على أمثلة للاستخدام من هذه المقالة). لتثبيته على جهاز كمبيوتر ، يمكنك استخدام الأمر في الطرفية:
تثبيت نقطة رسائل vk

الآن يمكننا استيراد الفئة الرئيسية من هذه الحزمة وإنشاء مثيل لها ، وتحديد تسجيل الدخول وكلمة المرور ونوع التفويض الذي يستخدمه هذا الحساب ، بالإضافة إلى الدليل حيث نريد حفظ ملفات تعريف ارتباط تخويل المستخدم. يعد ذلك ضروريًا حتى لا يضطر المستخدمون الحاصلون على تفويض من عاملين إلى إدخال الرمز من الرسالة باستمرار في كل مرة يتم فيها تشغيل البرنامج النصي.

from vk_messages import MessagesAPI

login, password = 'login', 'password'                                
messages = MessagesAPI(login=login, password=password,
                                two_factor=True, cookies_save_path='sessions/')

وفي الواقع ، هذا كل شيء. علينا الآن فقط فتح الوثائق واستخدام الأساليب التي نهتم بها. أود أن أشير على الفور إلى أن هذا النهج يسمح لنا باستخدام أي طريقة تقريبًا من الوثائق ، حتى لا تتعلق بقسم الرسائل:

history = messages.method('messages.getHistory', user_id='1234567', count=5)

يمكننا أيضًا دمج هذه المكتبة مع الآخرين ، على سبيل المثال ، من خلال vk_api يمكننا تحميل الصور من جهاز كمبيوتر (يتم توفير رمز هذا الإجراء في قسم الأمثلة الخاصة بهم) ، ومن خلال vk_messages إرفاق هذه المرفقات بالرسالة:

from vk_messages.utils import get_random

messages.method('messages.send', user_id=peer_id, message='Hello',
            attachment='photo123456_7891011', random_id=get_random())

بدافع الفضول ، قمت بتطبيق الوظيفة الكلاسيكية ، التي تنشئ في مجلد معين مجلدات فرعية من الأشخاص الذين كان الشخص يتحدث معهم ، ويحاول ضخ أحدث الرسائل وعناوين URL المطلقة للصور. لحسن الحظ ، عمل كل شيء مثل الساعة ، ولم تكن هناك أخطاء غير ضرورية:

from vk_messages.utils import fast_parser

fast_parser(messages, path='parsing/',                    
       count_conv=10, messages_deep=400, photos_deep=100) 

الآن أريد أن أنتقل إلى أحد الأجزاء الأكثر إثارة للاهتمام في هذه المكتبة: وجود ملفات تعريف ارتباط التفويض ، يمكننا تنفيذ أي إجراء على الإطلاق. اسمحوا لي أن أقدم لكم مثالاً شخصياً ، عندما كان علي أن أضع جدولاً يحتوي على معرف المشاركة ومؤلفها لمشاركات المجموعة التي أنا عضو فيها . ولكن ما كان الصيد: فالواجهة الرسمية تعيد فقط الشخص الذي نشر المقال. باستخدام الشم ، رأيت أنه عند التحويم في تاريخ النشر ، يتم تنزيل هذه البيانات من الخادم. وبعد ذلك كتبتُ غلافًا يسمح لك بإرسال العديد من الطلبات المماثلة كما تشاء ، باستخدام رابط المشاركة وملفات تعريف الارتباط للتفويض فقط للحصول على المؤلفين. في المثال أدناه ، يبقى فقط للتخلص من العلامات غير الضرورية:

def get_creators(post, cookies):
    group = -int(post.split('_')[0])
    response = requests.post('https://vk.com/al_page.php', cookies=cookies,
               data=f"_ads_group_id={group}&act=post_author_data_tt&al=1&raw={post}")

    response_json = json.loads(response.text[4:])['payload'][1]
    return response_json[0]

authors = get_creators(post='-12345_67890',
                    cookies=messages.get_cookies())
print(authors)   

ولكن ماذا يثبت لنا الجزء العلوي من الكود؟ هذا صحيح ، حتى إذا أغلق VC طلبات الاختبار على وثائقه ، يمكننا دائمًا محاكاة إجراءات المستخدم والحصول على المعلومات اللازمة. كتجربة ، قمت بعمل صغير يمكن من خلال طلبات "التمرير" للصفحة ، تلقي روابط للصور دون استخدام واجهة برمجة التطبيقات الرسمية.

كان الرمز كبيرًا جدًا ، لذلك قررت إخفاءه
def get_attachments(attachment_type, peer_id, count, offset, cookies_final):
    if attachment_type == 'photo':
        session = requests.Session()
        parsed = []
        
        response = session.post(f'https://vk.com/wkview.php',
                            data=f'act=show&al=1&dmcah=&loc=im&ref=&w=history{peer_id}_photo', cookies=cookies_final)
        
        response_json = json.loads(response.text[4:])
        
        try:
            last_offset = response_json['payload'][1][2]['offset']
            count_all = response_json['payload'][1][2]['count']
        except:
            last_offset = response_json['payload'][1][0]['offset']
            count_all = response_json['payload'][1][0]['count']

        while (len(parsed) < count + offset)  and (last_offset != count_all):
            response_json = json.loads(response.text[4:])
        
            try:
                last_offset = response_json['payload'][1][2]['offset']
            except:
                last_offset = response_json['payload'][1][0]['offset']

            photos_vk =  re.findall(r'<a href="/photo(\S*)?all=1"', response_json['payload'][1][1])
            mails =  re.findall(r"'(\S*)', {img: this ,", response_json['payload'][1][1])

            for photo, mail in zip(photos_vk, mails):
                if len(parsed) < offset:
                    parsed.append(photo)
                    continue
                
                response = session.post(f'https://vk.com/al_photos.php', cookies=cookies_final,
                            data=f'act=show&al=1&al_ad=0&dmcah=&gid=0&list={mail}&module=im&photo={photo}')
                
                response_json = json.loads(response.text[4:])
                photo_size = list(response_json['payload'][1][3][0].items())
                photo_size.reverse()
                
                for i in range(len(photo_size)):
                    if 'attached_tags' in photo_size[i][0]:
                        photo_size = photo_size[:i]
                        break
            
                parsed.append(photo_size) 

            response = session.post(f'https://vk.com/wkview.php', cookies=cookies_final,
                    data=f'act=show&al=1&offset={last_offset}&part=1&w=history{peer_id}_photo')
        
        return parsed[offset + 3 : offset + 3 + count]


هل تبدو ضخمة؟ نعم. هل يعمل أبطأ بكثير من واجهة برمجة التطبيقات الرسمية؟ نعم. ولكن إذا استغلت VC الفرصة الأخيرة للوصول إلى الرسائل ، فيمكننا دائمًا إيجاد مخرج.

ألاحظ أيضًا أنني حاولت الإضافة إلى جميع الأماكن في المكتبة حيث الأخطاء ممكنة ، مع استثناءات مع التفسيرات ، ولكن إذا وجدت أي أحداث لا تعرض شرحًا ، فيرجى إبلاغي بذلك.
, , - , , , , , . , , .

?


بالنسبة لأولئك المهتمين بما يحدث تحت غطاء هذا البرنامج النصي ، سأستعرض باختصار النقاط الرئيسية. أثناء التفويض ، يتم إجراء طلبات طلب بسيطة تحاكي تسجيل دخول المستخدم ، والذي يتغير قليلاً اعتمادًا على نوع التفويض ، وبعد تسجيل الدخول الناجح ، يتم حفظ ملفات تعريف الارتباط في ملف مخلل. عند طلب واجهة برمجة تطبيقات من خلال الوثائق ، تتم إضافة "param_" إلى جميع المعلمات المخصصة ، أي أن قيمة الإزاحة ستتحول إلى param_offset. أيضًا ، يتم إرسال رمز التجزئة في الطلب ، والذي يتم تضمينه في سمة تجزئة البيانات لعلامة الزر "تشغيل". بقدر ما لاحظت ، هذه القيمة ثابتة لكل طريقة.

سألاحظ أيضًا نقطة مهمة واحدة: يتم إرسال كلمة المرور بترميز ANSI ، حيث يتم فصل أحرف الأبجدية الروسية عن طريق علامة "٪" ، وهذا الرمز يكفي لتنفيذ هذا فك التشفير. يمكن أن يكون هذا مشكلة لبعض مستخدمي Linux ، لأنه على حد ما أتذكر ، لا يتم تضمين هذا الترميز افتراضيًا في python على نظام التشغيل هذا.

self.password = str(password.encode('ANSI')).replace('\\x', '%')[2:-1]

أيضا ، كانت إحدى المشاكل بالنسبة لي هي السلوك الغريب لبعض الطرق. على سبيل المثال ، إذا قمت بتبديل المعلمات ، فقد يقوم البرنامج النصي بإرجاع استجابة أصغر بعشر مرات من الاستجابة المطلوبة أو عدم إرجاع أي شيء على الإطلاق. لحل هذه المشكلة ، قررت للتو تحليل المعلمات وإرسالها بترتيب صارم ، كما هو موضح في الوثائق. ربما هذه مصادفة بسيطة ، ولكن بعد هذه المشاكل لم يكن لدي:

response = session.get(f'https://vk.com/dev/{name}', cookies=self.cookies_final)
hash_data =  re.findall(r'data-hash="(\S*)"', response.text)

soup = BeautifulSoup(response.text, features="html.parser")
params = soup.findAll("div", {"class": "dev_const_param_name"})
params = [cleanhtml(str(i)) for i in params]

payload, checker = '', 0
for param in params:
   if param in kwargs:
       checker += 1
       payload += '&{}={}'.format('param_' + \
   param, quote(str(kwargs[param]) if type(kwargs[param]) != bool else str(int(kwargs[param]))))
        
if checker != len(kwargs):
   raise Exception_MessagesAPI('Some of the parametrs invalid', 'InvalidParameters')

مجموع


حسنًا ، بالنسبة لي ، كانت هذه المكتبة أول تجربة لكتابة مشاريع "مفتوحة" ، لذا أطلب منك عدم الحكم عليها بشدة. أردت فقط مساعدة الأشخاص الذين يواجهون نفس المشكلة مثلي: قيود واجهة برمجة تطبيقات الرسائل. أريد أيضًا أن أشكر أصدقائي الذين ساعدوني في كتابة هذه المقالة واختبار الرمز.

All Articles