commit
332cb3733c
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Justuser
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,27 @@
|
|||||||
|
# mrp_bot
|
||||||
|
Бот парсер онлайна проекта Mordor Role Play.
|
||||||
|
VK, TG
|
||||||
|
## Установка зависимостей:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
## Что требуется для запуска ботов?
|
||||||
|
Создать файл .env:
|
||||||
|
```bash
|
||||||
|
touch .env
|
||||||
|
```
|
||||||
|
Заполнить следующие переменные любым текстовым редактором:
|
||||||
|
* tg_token = "API токен телеграм бота"
|
||||||
|
* vk_token = "API токен вк бота"
|
||||||
|
* vk_group_id = 123
|
||||||
|
|
||||||
|
ПРИМЕЧАНИЕ: вместо 123 впишите айди группы (тип - int)
|
||||||
|
|
||||||
|
---
|
||||||
|
Запустить ботов командами:
|
||||||
|
```bash
|
||||||
|
nohup python3 vk_bot.py &
|
||||||
|
nohup python3 tg_bot.py &
|
||||||
|
```
|
||||||
|
## Основная команда ботов
|
||||||
|
* /mrp_online - получение онлайна
|
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0"}
|
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'python273'
|
||||||
|
__version__ = '3.0'
|
||||||
|
__email__ = 'vk_api@python273.pw'
|
||||||
|
|
||||||
|
from .jconfig import Config
|
||||||
|
from .memory import MemoryConfig
|
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from .base import BaseConfig
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseConfig):
|
||||||
|
""" Класс конфигурации в файле
|
||||||
|
|
||||||
|
:param filename: имя файла
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('_filename',)
|
||||||
|
|
||||||
|
def __init__(self, section, filename='.jconfig'):
|
||||||
|
self._filename = filename
|
||||||
|
|
||||||
|
super(Config, self).__init__(section, filename=filename)
|
||||||
|
|
||||||
|
def load(self, filename, **kwargs):
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
settings = json.load(f)
|
||||||
|
except (IOError, ValueError):
|
||||||
|
settings = {}
|
||||||
|
|
||||||
|
settings.setdefault(self.section_name, {})
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
with open(self._filename, 'w') as f:
|
||||||
|
json.dump(self._settings, f, indent=2, sort_keys=True)
|
@ -0,0 +1,5 @@
|
|||||||
|
telebot
|
||||||
|
vk_api
|
||||||
|
requests
|
||||||
|
random
|
||||||
|
python-dotenv
|
@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import telebot
|
||||||
|
from os import getenv
|
||||||
|
from requests import get
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from pathlib import Path
|
||||||
|
dotenv_path = f"{Path(__file__).parent.resolve()}/.env"
|
||||||
|
load_dotenv(dotenv_path=dotenv_path)
|
||||||
|
bot = telebot.TeleBot(getenv("tg_token"))
|
||||||
|
|
||||||
|
@bot.message_handler(commands=["mrp_online"])
|
||||||
|
def get_online(message):
|
||||||
|
try:
|
||||||
|
response = get("https://l.mordor-rp.com/launcher/monitoring/online.php").json()
|
||||||
|
rponl = 0; obsonl = 0; funonl = 0; text = ""
|
||||||
|
for element in response:
|
||||||
|
text += f'[{element["name"]}]: {element["min"]}\n'
|
||||||
|
obsonl += int(element["min"])
|
||||||
|
if element["tag"] == "roleplay": rponl += int(element["min"])
|
||||||
|
if element["tag"] == "fun": funonl += int(element["min"])
|
||||||
|
text += f"========================\n" \
|
||||||
|
f"ROLEPLAY ONLINE: {rponl}\n" \
|
||||||
|
f"FUN ONLINE: {funonl}\n" \
|
||||||
|
f"========================\n" \
|
||||||
|
f"FULL ONLINE: {obsonl}"
|
||||||
|
bot.reply_to(message, text)
|
||||||
|
except Exception:
|
||||||
|
bot.reply_to(message, "Ошибка.")
|
||||||
|
|
||||||
|
@bot.message_handler(commands=['start', 'help'])
|
||||||
|
def process_start_command(message):
|
||||||
|
bot.reply_to(
|
||||||
|
message,
|
||||||
|
f"Команды:\n"\
|
||||||
|
f"- /my_id - получение уникального идентификатора пользователя.\n"\
|
||||||
|
f"- /mrp_online - получение онлайна на проекте Mordor Role Play"
|
||||||
|
)
|
||||||
|
|
||||||
|
@bot.message_handler(commands=["my_id"])
|
||||||
|
def user_id(message):
|
||||||
|
uid = message.from_user.id
|
||||||
|
bot.reply_to(
|
||||||
|
message,
|
||||||
|
f"<a href='tg://user?id={uid}'>Пользователь</a>, "\
|
||||||
|
f"твой уникальный идентификатор (ID):\n\n"\
|
||||||
|
f"<code>{uid}</code>",
|
||||||
|
parse_mode="HTML"
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
while True:
|
||||||
|
try: bot.polling()
|
||||||
|
except KeyboardInterrupt: exit()
|
||||||
|
except: pass
|
@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
from .enums import *
|
||||||
|
from .exceptions import *
|
||||||
|
from .requests_pool import VkRequestsPool, vk_request_one_param_pool
|
||||||
|
from .tools import VkTools
|
||||||
|
from .upload import VkUpload
|
||||||
|
from .vk_api import VkApi
|
||||||
|
|
||||||
|
|
||||||
|
__author__ = 'python273'
|
||||||
|
__version__ = '11.9.9'
|
||||||
|
__email__ = 'vk_api@python273.pw'
|
@ -0,0 +1,682 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from .audio_url_decoder import decode_audio_url
|
||||||
|
from .exceptions import AccessDenied
|
||||||
|
from .utils import set_cookies_from_list
|
||||||
|
|
||||||
|
RE_ALBUM_ID = re.compile(r'act=audio_playlist(-?\d+)_(\d+)')
|
||||||
|
RE_ACCESS_HASH = re.compile(r'access_hash=(\w+)')
|
||||||
|
RE_M3U8_TO_MP3 = re.compile(r'/[0-9a-f]+(/audios)?/([0-9a-f]+)/index.m3u8')
|
||||||
|
|
||||||
|
RPS_DELAY_RELOAD_AUDIO = 1.5
|
||||||
|
RPS_DELAY_LOAD_SECTION = 2.0
|
||||||
|
|
||||||
|
TRACKS_PER_USER_PAGE = 2000
|
||||||
|
TRACKS_PER_ALBUM_PAGE = 2000
|
||||||
|
ALBUMS_PER_USER_PAGE = 100
|
||||||
|
|
||||||
|
|
||||||
|
class VkAudio(object):
|
||||||
|
""" Модуль для получения аудиозаписей без использования официального API.
|
||||||
|
|
||||||
|
:param vk: Объект :class:`VkApi`
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('_vk', 'user_id', 'convert_m3u8_links')
|
||||||
|
|
||||||
|
DEFAULT_COOKIES = [
|
||||||
|
{ # если не установлено, то первый запрос ломается
|
||||||
|
'version': 0,
|
||||||
|
'name': 'remixaudio_show_alert_today',
|
||||||
|
'value': '0',
|
||||||
|
'port': None,
|
||||||
|
'port_specified': False,
|
||||||
|
'domain': '.vk.com',
|
||||||
|
'domain_specified': True,
|
||||||
|
'domain_initial_dot': True,
|
||||||
|
'path': '/',
|
||||||
|
'path_specified': True,
|
||||||
|
'secure': True,
|
||||||
|
'expires': None,
|
||||||
|
'discard': False,
|
||||||
|
'comment': None,
|
||||||
|
'comment_url': None,
|
||||||
|
'rfc2109': False,
|
||||||
|
'rest': {}
|
||||||
|
}, { # для аудио из постов
|
||||||
|
'version': 0,
|
||||||
|
'name': 'remixmdevice',
|
||||||
|
'value': '1920/1080/2/!!-!!!!',
|
||||||
|
'port': None,
|
||||||
|
'port_specified': False,
|
||||||
|
'domain': '.vk.com',
|
||||||
|
'domain_specified': True,
|
||||||
|
'domain_initial_dot': True,
|
||||||
|
'path': '/',
|
||||||
|
'path_specified': True,
|
||||||
|
'secure': True,
|
||||||
|
'expires': None,
|
||||||
|
'discard': False,
|
||||||
|
'comment': None,
|
||||||
|
'comment_url': None,
|
||||||
|
'rfc2109': False,
|
||||||
|
'rest': {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, vk, convert_m3u8_links=True):
|
||||||
|
self.user_id = vk.method('users.get')[0]['id']
|
||||||
|
self._vk = vk
|
||||||
|
self.convert_m3u8_links = convert_m3u8_links
|
||||||
|
|
||||||
|
set_cookies_from_list(self._vk.http.cookies, self.DEFAULT_COOKIES)
|
||||||
|
|
||||||
|
self._vk.http.get('https://m.vk.com/') # load cookies
|
||||||
|
|
||||||
|
def get_iter(self, owner_id=None, album_id=None, access_hash=None):
|
||||||
|
""" Получить список аудиозаписей пользователя (по частям)
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
:param album_id: ID альбома
|
||||||
|
:param access_hash: ACCESS_HASH альбома
|
||||||
|
"""
|
||||||
|
|
||||||
|
if owner_id is None:
|
||||||
|
owner_id = self.user_id
|
||||||
|
|
||||||
|
if album_id is not None:
|
||||||
|
offset_diff = TRACKS_PER_ALBUM_PAGE
|
||||||
|
else:
|
||||||
|
offset_diff = TRACKS_PER_USER_PAGE
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
while True:
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://m.vk.com/audio',
|
||||||
|
data={
|
||||||
|
'act': 'load_section',
|
||||||
|
'owner_id': owner_id,
|
||||||
|
'playlist_id': album_id if album_id else -1,
|
||||||
|
'offset': offset,
|
||||||
|
'type': 'playlist',
|
||||||
|
'access_hash': access_hash,
|
||||||
|
'is_loading_all': 1
|
||||||
|
},
|
||||||
|
allow_redirects=False
|
||||||
|
).json()
|
||||||
|
|
||||||
|
if not response['data'][0]:
|
||||||
|
raise AccessDenied(
|
||||||
|
'You don\'t have permissions to browse {}\'s albums'.format(
|
||||||
|
owner_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
response['data'][0]['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
self._vk.http,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tracks:
|
||||||
|
break
|
||||||
|
|
||||||
|
for i in tracks:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
if response['data'][0]['hasMore']:
|
||||||
|
offset += offset_diff
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def get(self, owner_id=None, album_id=None, access_hash=None):
|
||||||
|
""" Получить список аудиозаписей пользователя
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
:param album_id: ID альбома
|
||||||
|
:param access_hash: ACCESS_HASH альбома
|
||||||
|
"""
|
||||||
|
|
||||||
|
return list(self.get_iter(owner_id, album_id, access_hash))
|
||||||
|
|
||||||
|
def get_albums_iter(self, owner_id=None):
|
||||||
|
""" Получить список альбомов пользователя (по частям)
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if owner_id is None:
|
||||||
|
owner_id = self.user_id
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
response = self._vk.http.get(
|
||||||
|
'https://m.vk.com/audio?act=audio_playlists{}'.format(
|
||||||
|
owner_id
|
||||||
|
),
|
||||||
|
params={
|
||||||
|
'offset': offset
|
||||||
|
},
|
||||||
|
allow_redirects=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if not response.text:
|
||||||
|
raise AccessDenied(
|
||||||
|
'You don\'t have permissions to browse {}\'s albums'.format(
|
||||||
|
owner_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
albums = scrap_albums(response.text)
|
||||||
|
|
||||||
|
if not albums:
|
||||||
|
break
|
||||||
|
|
||||||
|
for i in albums:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
offset += ALBUMS_PER_USER_PAGE
|
||||||
|
|
||||||
|
def get_albums(self, owner_id=None):
|
||||||
|
""" Получить список альбомов пользователя
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return list(self.get_albums_iter(owner_id))
|
||||||
|
|
||||||
|
def search_user(self, owner_id=None, q=''):
|
||||||
|
""" Искать по аудиозаписям пользователя
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
:param q: запрос
|
||||||
|
"""
|
||||||
|
|
||||||
|
if owner_id is None:
|
||||||
|
owner_id = self.user_id
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'section',
|
||||||
|
'claim': 0,
|
||||||
|
'is_layer': 0,
|
||||||
|
'owner_id': owner_id,
|
||||||
|
'section': 'search',
|
||||||
|
'q': q
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
if not json_response['payload'][1]:
|
||||||
|
raise AccessDenied(
|
||||||
|
'You don\'t have permissions to browse {}\'s audio'.format(
|
||||||
|
owner_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if json_response['payload'][1][1]['playlists']:
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
json_response['payload'][1][1]['playlists'][0]['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
self._vk.http,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links
|
||||||
|
)
|
||||||
|
|
||||||
|
return list(tracks)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def search(self, q, count=100, offset=0):
|
||||||
|
""" Искать аудиозаписи
|
||||||
|
|
||||||
|
:param q: запрос
|
||||||
|
:param count: количество
|
||||||
|
:param offset: смещение
|
||||||
|
"""
|
||||||
|
|
||||||
|
return islice(self.search_iter(q, offset=offset), count)
|
||||||
|
|
||||||
|
def search_iter(self, q, offset=0):
|
||||||
|
""" Искать аудиозаписи (генератор)
|
||||||
|
|
||||||
|
:param q: запрос
|
||||||
|
:param offset: смещение
|
||||||
|
"""
|
||||||
|
offset_left = 0
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'section',
|
||||||
|
'claim': 0,
|
||||||
|
'is_layer': 0,
|
||||||
|
'owner_id': self.user_id,
|
||||||
|
'section': 'search',
|
||||||
|
'q': q
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
while json_response['payload'][1][1]['playlist']:
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
json_response['payload'][1][1]['playlist']['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
if offset_left + len(ids) >= offset:
|
||||||
|
if offset_left < offset:
|
||||||
|
ids = ids[offset - offset_left:]
|
||||||
|
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tracks:
|
||||||
|
break
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
yield track
|
||||||
|
|
||||||
|
offset_left += len(ids)
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'load_catalog_section',
|
||||||
|
'section_id': json_response['payload'][1][1]['sectionId'],
|
||||||
|
'start_from': json_response['payload'][1][1]['nextFrom']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
def get_updates_iter(self):
|
||||||
|
""" Искать обновления друзей (генератор) """
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'section',
|
||||||
|
'claim': 0,
|
||||||
|
'is_layer': 0,
|
||||||
|
'owner_id': self.user_id,
|
||||||
|
'section': 'updates'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
updates = [i['list'] for i in json_response['payload'][1][1]['playlists']]
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
[i[0] for i in updates if i]
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tracks:
|
||||||
|
break
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
yield track
|
||||||
|
|
||||||
|
if len(updates) < 11:
|
||||||
|
break
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'load_catalog_section',
|
||||||
|
'section_id': json_response['payload'][1][1]['sectionId'],
|
||||||
|
'start_from': json_response['payload'][1][1]['nextFrom']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
def get_popular_iter(self, offset=0):
|
||||||
|
""" Искать популярные аудиозаписи (генератор)
|
||||||
|
|
||||||
|
:param offset: смещение
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/audio',
|
||||||
|
data={
|
||||||
|
'block': 'chart',
|
||||||
|
'section': 'explore'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(scrap_json(response.text))
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
json_response['sectionData']['explore']['playlist']['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
if offset:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids[offset:],
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
yield track
|
||||||
|
|
||||||
|
def get_news_iter(self, offset=0):
|
||||||
|
""" Искать популярные аудиозаписи (генератор)
|
||||||
|
|
||||||
|
:param offset: смещение
|
||||||
|
"""
|
||||||
|
|
||||||
|
offset_left = 0
|
||||||
|
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/audio',
|
||||||
|
data={
|
||||||
|
'block': 'new_songs',
|
||||||
|
'section': 'explore'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
json_response = json.loads(scrap_json(response.text))
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
json_response['sectionData']['explore']['playlist']['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
if offset_left + len(ids) >= offset:
|
||||||
|
if offset_left >= offset:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids[offset - offset_left:],
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
yield track
|
||||||
|
|
||||||
|
offset_left += len(ids)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
response = self._vk.http.post(
|
||||||
|
'https://vk.com/al_audio.php',
|
||||||
|
data={
|
||||||
|
'al': 1,
|
||||||
|
'act': 'load_catalog_section',
|
||||||
|
'section_id': json_response['sectionData']['explore']['sectionId'],
|
||||||
|
'start_from': json_response['sectionData']['explore']['nextFrom']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
json_response = json.loads(response.text.replace('<!--', ''))
|
||||||
|
|
||||||
|
ids = scrap_ids(
|
||||||
|
json_response['payload'][1][1]['playlist']['list']
|
||||||
|
)
|
||||||
|
|
||||||
|
if offset_left + len(ids) >= offset:
|
||||||
|
if offset_left >= offset:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids[offset - offset_left:],
|
||||||
|
self.user_id,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links,
|
||||||
|
http=self._vk.http
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tracks:
|
||||||
|
break
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
yield track
|
||||||
|
|
||||||
|
offset_left += len(ids)
|
||||||
|
|
||||||
|
def get_audio_by_id(self, owner_id, audio_id):
|
||||||
|
""" Получить аудиозапись по ID
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
:param audio_id: ID аудио
|
||||||
|
"""
|
||||||
|
response = self._vk.http.get(
|
||||||
|
'https://m.vk.com/audio{}_{}'.format(owner_id, audio_id),
|
||||||
|
allow_redirects=False
|
||||||
|
)
|
||||||
|
|
||||||
|
ids = scrap_ids_from_html(
|
||||||
|
response.text,
|
||||||
|
filter_root_el={'class': 'basisDefault'}
|
||||||
|
)
|
||||||
|
|
||||||
|
track = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
http=self._vk.http,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links
|
||||||
|
)
|
||||||
|
|
||||||
|
if track:
|
||||||
|
return next(track)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_post_audio(self, owner_id, post_id):
|
||||||
|
""" Получить список аудиозаписей из поста пользователя или группы
|
||||||
|
|
||||||
|
:param owner_id: ID владельца (отрицательные значения для групп)
|
||||||
|
:param post_id: ID поста
|
||||||
|
"""
|
||||||
|
response = self._vk.http.get(
|
||||||
|
'https://m.vk.com/wall{}_{}'.format(owner_id, post_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
ids = scrap_ids_from_html(
|
||||||
|
response.text,
|
||||||
|
filter_root_el={'class': 'audios_list'}
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = scrap_tracks(
|
||||||
|
ids,
|
||||||
|
self.user_id,
|
||||||
|
http=self._vk.http,
|
||||||
|
convert_m3u8_links=self.convert_m3u8_links
|
||||||
|
)
|
||||||
|
|
||||||
|
return tracks
|
||||||
|
|
||||||
|
|
||||||
|
def scrap_ids(audio_data):
|
||||||
|
""" Парсинг списка хэшей аудиозаписей из json объекта """
|
||||||
|
ids = []
|
||||||
|
|
||||||
|
for track in audio_data:
|
||||||
|
audio_hashes = track[13].split("/")
|
||||||
|
|
||||||
|
full_id = (
|
||||||
|
str(track[1]), str(track[0]), audio_hashes[2], audio_hashes[5]
|
||||||
|
)
|
||||||
|
if all(full_id):
|
||||||
|
ids.append(full_id)
|
||||||
|
|
||||||
|
return ids
|
||||||
|
|
||||||
|
|
||||||
|
def scrap_json(html_page):
|
||||||
|
""" Парсинг списка хэшей ауфдиозаписей новинок или популярных + nextFrom&sessionId """
|
||||||
|
|
||||||
|
find_json_pattern = r"new AudioPage\(.*?(\{.*\})"
|
||||||
|
fr = re.search(find_json_pattern, html_page).group(1)
|
||||||
|
|
||||||
|
return fr
|
||||||
|
|
||||||
|
|
||||||
|
def scrap_ids_from_html(html, filter_root_el=None):
|
||||||
|
""" Парсинг списка хэшей аудиозаписей из html страницы """
|
||||||
|
|
||||||
|
if filter_root_el is None:
|
||||||
|
filter_root_el = {'id': 'au_search_items'}
|
||||||
|
|
||||||
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
|
ids = []
|
||||||
|
|
||||||
|
root_el = soup.find(**filter_root_el)
|
||||||
|
|
||||||
|
if root_el is None:
|
||||||
|
raise ValueError('Could not find root el for audio')
|
||||||
|
|
||||||
|
playlist_snippets = soup.find_all('div', {'class': "audioPlaylistSnippet__list"})
|
||||||
|
for playlist in playlist_snippets:
|
||||||
|
playlist.decompose()
|
||||||
|
|
||||||
|
for audio in root_el.find_all('div', {'class': 'audio_item'}):
|
||||||
|
if 'audio_item_disabled' in audio['class']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data_audio = json.loads(audio['data-audio'])
|
||||||
|
audio_hashes = data_audio[13].split("/")
|
||||||
|
|
||||||
|
full_id = (
|
||||||
|
str(data_audio[1]), str(data_audio[0]), audio_hashes[2], audio_hashes[5]
|
||||||
|
)
|
||||||
|
|
||||||
|
if all(full_id):
|
||||||
|
ids.append(full_id)
|
||||||
|
|
||||||
|
return ids
|
||||||
|
|
||||||
|
|
||||||
|
def scrap_tracks(ids, user_id, http, convert_m3u8_links=True):
|
||||||
|
|
||||||
|
last_request = 0.0
|
||||||
|
|
||||||
|
for ids_group in [ids[i:i + 10] for i in range(0, len(ids), 10)]:
|
||||||
|
delay = RPS_DELAY_RELOAD_AUDIO - (time.time() - last_request)
|
||||||
|
|
||||||
|
if delay > 0:
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
result = http.post(
|
||||||
|
'https://m.vk.com/audio',
|
||||||
|
data={'act': 'reload_audio', 'ids': ','.join(['_'.join(i) for i in ids_group])}
|
||||||
|
).json()
|
||||||
|
|
||||||
|
last_request = time.time()
|
||||||
|
if result['data']:
|
||||||
|
data_audio = result['data'][0]
|
||||||
|
for audio in data_audio:
|
||||||
|
artist = BeautifulSoup(audio[4], 'html.parser').text
|
||||||
|
title = BeautifulSoup(audio[3].strip(), 'html.parser').text
|
||||||
|
duration = audio[5]
|
||||||
|
link = audio[2]
|
||||||
|
|
||||||
|
if 'audio_api_unavailable' in link:
|
||||||
|
link = decode_audio_url(link, user_id)
|
||||||
|
|
||||||
|
if convert_m3u8_links and 'm3u8' in link:
|
||||||
|
link = RE_M3U8_TO_MP3.sub(r'\1/\2.mp3', link)
|
||||||
|
|
||||||
|
yield {
|
||||||
|
'id': audio[0],
|
||||||
|
'owner_id': audio[1],
|
||||||
|
'track_covers': audio[14].split(',') if audio[14] else [],
|
||||||
|
'url': link,
|
||||||
|
|
||||||
|
'artist': artist,
|
||||||
|
'title': title,
|
||||||
|
'duration': duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def scrap_albums(html):
|
||||||
|
""" Парсинг списка альбомов из html страницы """
|
||||||
|
|
||||||
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
|
albums = []
|
||||||
|
|
||||||
|
for album in soup.find_all('div', {'class': 'audioPlaylistsPage__item'}):
|
||||||
|
|
||||||
|
link = album.select_one('.audioPlaylistsPage__itemLink')['href']
|
||||||
|
full_id = tuple(int(i) for i in RE_ALBUM_ID.search(link).groups())
|
||||||
|
access_hash = RE_ACCESS_HASH.search(link)
|
||||||
|
|
||||||
|
stats_text = album.select_one('.audioPlaylistsPage__stats').text
|
||||||
|
|
||||||
|
# "1 011 прослушиваний"
|
||||||
|
try:
|
||||||
|
plays = int(stats_text.rsplit(' ', 1)[0].replace(' ', ''))
|
||||||
|
except ValueError:
|
||||||
|
plays = None
|
||||||
|
|
||||||
|
albums.append({
|
||||||
|
'id': full_id[1],
|
||||||
|
'owner_id': full_id[0],
|
||||||
|
'url': 'https://m.vk.com/audio?act=audio_playlist{}_{}'.format(
|
||||||
|
*full_id
|
||||||
|
),
|
||||||
|
'access_hash': access_hash.group(1) if access_hash else None,
|
||||||
|
|
||||||
|
'title': album.select_one('.audioPlaylistsPage__title').text,
|
||||||
|
'artist': album.select_one('.audioPlaylistsPage__author').text,
|
||||||
|
'plays': plays
|
||||||
|
})
|
||||||
|
|
||||||
|
return albums
|
@ -0,0 +1,141 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .exceptions import VkAudioUrlDecodeError
|
||||||
|
|
||||||
|
VK_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN0PQRSTUVWXYZO123456789+/="
|
||||||
|
|
||||||
|
|
||||||
|
def splice(l, a, b, c):
|
||||||
|
""" JS's Array.prototype.splice
|
||||||
|
|
||||||
|
var x = [1, 2, 3],
|
||||||
|
y = x.splice(0, 2, 1337);
|
||||||
|
|
||||||
|
eq
|
||||||
|
|
||||||
|
x = [1, 2, 3]
|
||||||
|
x, y = splice(x, 0, 2, 1337)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return l[:a] + [c] + l[a + b:], l[a:a + b]
|
||||||
|
|
||||||
|
|
||||||
|
def decode_audio_url(string, user_id):
|
||||||
|
vals = string.split("?extra=", 1)[1].split("#")
|
||||||
|
|
||||||
|
tstr = vk_o(vals[0])
|
||||||
|
ops_list = vk_o(vals[1]).split('\x09')[::-1]
|
||||||
|
|
||||||
|
for op_data in ops_list:
|
||||||
|
|
||||||
|
split_op_data = op_data.split('\x0b')
|
||||||
|
cmd = split_op_data[0]
|
||||||
|
if len(split_op_data) > 1:
|
||||||
|
arg = split_op_data[1]
|
||||||
|
else:
|
||||||
|
arg = None
|
||||||
|
|
||||||
|
if cmd == 'v':
|
||||||
|
tstr = tstr[::-1]
|
||||||
|
|
||||||
|
elif cmd == 'r':
|
||||||
|
tstr = vk_r(tstr, arg)
|
||||||
|
|
||||||
|
elif cmd == 'x':
|
||||||
|
tstr = vk_xor(tstr, arg)
|
||||||
|
elif cmd == 's':
|
||||||
|
tstr = vk_s(tstr, arg)
|
||||||
|
elif cmd == 'i':
|
||||||
|
tstr = vk_i(tstr, arg, user_id)
|
||||||
|
else:
|
||||||
|
raise VkAudioUrlDecodeError(
|
||||||
|
'Unknown decode cmd: "{}"; Please send bugreport'.format(cmd)
|
||||||
|
)
|
||||||
|
|
||||||
|
return tstr
|
||||||
|
|
||||||
|
|
||||||
|
def vk_o(string):
|
||||||
|
result = []
|
||||||
|
index2 = 0
|
||||||
|
|
||||||
|
for s in string:
|
||||||
|
sym_index = VK_STR.find(s)
|
||||||
|
|
||||||
|
if sym_index != -1:
|
||||||
|
if index2 % 4 != 0:
|
||||||
|
i = (i << 6) + sym_index
|
||||||
|
else:
|
||||||
|
i = sym_index
|
||||||
|
|
||||||
|
if index2 % 4 != 0:
|
||||||
|
index2 += 1
|
||||||
|
shift = -2 * index2 & 6
|
||||||
|
result += [chr(0xFF & (i >> shift))]
|
||||||
|
else:
|
||||||
|
index2 += 1
|
||||||
|
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
def vk_r(string, i):
|
||||||
|
vk_str2 = VK_STR + VK_STR
|
||||||
|
vk_str2_len = len(vk_str2)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for s in string:
|
||||||
|
index = vk_str2.find(s)
|
||||||
|
|
||||||
|
if index != -1:
|
||||||
|
offset = index - int(i)
|
||||||
|
|
||||||
|
if offset < 0:
|
||||||
|
offset += vk_str2_len
|
||||||
|
|
||||||
|
result += [vk_str2[offset]]
|
||||||
|
else:
|
||||||
|
result += [s]
|
||||||
|
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
def vk_xor(string, i):
|
||||||
|
xor_val = ord(i[0])
|
||||||
|
|
||||||
|
return ''.join(chr(ord(s) ^ xor_val) for s in string)
|
||||||
|
|
||||||
|
|
||||||
|
def vk_s_child(t, e):
|
||||||
|
i = len(t)
|
||||||
|
|
||||||
|
if not i:
|
||||||
|
return []
|
||||||
|
|
||||||
|
o = []
|
||||||
|
e = int(e)
|
||||||
|
|
||||||
|
for a in range(i - 1, -1, -1):
|
||||||
|
e = (i * (a + 1) ^ e + a) % i
|
||||||
|
o.append(e)
|
||||||
|
|
||||||
|
return o[::-1]
|
||||||
|
|
||||||
|
|
||||||
|
def vk_s(t, e):
|
||||||
|
i = len(t)
|
||||||
|
|
||||||
|
if not i:
|
||||||
|
return t
|
||||||
|
|
||||||
|
o = vk_s_child(t, e)
|
||||||
|
t = list(t)
|
||||||
|
|
||||||
|
for a in range(1, i):
|
||||||
|
t, y = splice(t, o[i - 1 - a], 1, t[a])
|
||||||
|
t[a] = y[0]
|
||||||
|
|
||||||
|
return ''.join(t)
|
||||||
|
|
||||||
|
|
||||||
|
def vk_i(t, e, user_id):
|
||||||
|
return vk_s(t, int(e) ^ user_id)
|
@ -0,0 +1,102 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .utils import sjson_dumps
|
||||||
|
from .vk_api import VkApi, VkApiMethod
|
||||||
|
|
||||||
|
|
||||||
|
class VkFunction(object):
|
||||||
|
""" Обертка над методом execute.
|
||||||
|
|
||||||
|
:param code: код функции (VKScript)
|
||||||
|
:param args: список аргументов (будут конвертированы в JSON)
|
||||||
|
:param clean_args: список raw аргументов (будут вставлены как строки)
|
||||||
|
:param return_raw: аргумент raw функции VkApi.method
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('code', '_minified_code', 'args', 'clean_args', 'return_raw')
|
||||||
|
|
||||||
|
def __init__(self, code, args=None, clean_args=None, return_raw=False):
|
||||||
|
self.code = code
|
||||||
|
self._minified_code = minify(code)
|
||||||
|
|
||||||
|
self.args = () if args is None else args
|
||||||
|
self.clean_args = () if clean_args is None else clean_args
|
||||||
|
|
||||||
|
self.return_raw = return_raw
|
||||||
|
|
||||||
|
def compile(self, args):
|
||||||
|
compiled_args = {}
|
||||||
|
|
||||||
|
for key, value in args.items():
|
||||||
|
if key in self.clean_args:
|
||||||
|
compiled_args[key] = str(value)
|
||||||
|
else:
|
||||||
|
compiled_args[key] = sjson_dumps(value)
|
||||||
|
|
||||||
|
return self._minified_code % compiled_args
|
||||||
|
|
||||||
|
def __call__(self, vk, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param vk: VkApi или VkApiMethod
|
||||||
|
:param \*args:
|
||||||
|
:param \*\*kwargs:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(vk, (VkApi, VkApiMethod)):
|
||||||
|
raise TypeError(
|
||||||
|
'The first arg should be VkApi or VkApiMethod instance'
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(vk, VkApiMethod):
|
||||||
|
vk = vk._vk
|
||||||
|
|
||||||
|
args = parse_args(self.args, args, kwargs)
|
||||||
|
|
||||||
|
return vk.method(
|
||||||
|
'execute',
|
||||||
|
{'code': self.compile(args)},
|
||||||
|
raw=self.return_raw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def minify(code):
|
||||||
|
return ''.join(i.strip() for i in code.splitlines())
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(function_args, args, kwargs):
|
||||||
|
parsed_args = {}
|
||||||
|
|
||||||
|
for arg_name in kwargs.keys():
|
||||||
|
if arg_name in function_args:
|
||||||
|
parsed_args[arg_name] = kwargs[arg_name]
|
||||||
|
else:
|
||||||
|
raise VkFunctionException(
|
||||||
|
'function got an unexpected keyword argument \'{}\''.format(
|
||||||
|
arg_name
|
||||||
|
))
|
||||||
|
|
||||||
|
args_count = len(args) + len(kwargs)
|
||||||
|
func_args_count = len(function_args)
|
||||||
|
|
||||||
|
if args_count != func_args_count:
|
||||||
|
raise VkFunctionException(
|
||||||
|
'function takes exactly {} argument{} ({} given)'.format(
|
||||||
|
func_args_count,
|
||||||
|
's' if func_args_count > 1 else '',
|
||||||
|
args_count
|
||||||
|
))
|
||||||
|
|
||||||
|
for arg_name, arg_value in zip(function_args, args):
|
||||||
|
parsed_args[arg_name] = arg_value
|
||||||
|
|
||||||
|
return parsed_args
|
||||||
|
|
||||||
|
|
||||||
|
class VkFunctionException(Exception):
|
||||||
|
pass
|
@ -0,0 +1,166 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:authors: python273
|
||||||
|
:license: Apache License, Version 2.0, see LICENSE file
|
||||||
|
|
||||||
|
:copyright: (c) 2019 python273
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as json
|
||||||
|
except ImportError:
|
||||||
|
import json
|
||||||
|
|
||||||
|
from http.cookiejar import Cookie
|
||||||
|
|
||||||
|
|
||||||
|
def search_re(reg, string):
|
||||||
|
""" Поиск по регулярке """
|
||||||
|
s = reg.search(string)
|
||||||
|
|
||||||
|
if s:
|
||||||
|
groups = s.groups()
|
||||||
|
return groups[0]
|
||||||
|
|
||||||
|
|
||||||
|
def clear_string(s):
|
||||||
|
if s:
|
||||||
|
return s.strip().replace(' ', '')
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_id():
|
||||||
|
""" Get random int32 number (signed) """
|
||||||
|
return random.getrandbits(31) * random.choice([-1, 1])
|
||||||
|
|
||||||
|
|
||||||
|
def code_from_number(prefix, postfix, number):
|
||||||
|
prefix_len = len(prefix)
|
||||||
|
postfix_len = len(postfix)
|
||||||
|
|
||||||
|
if number[0] == '+':
|
||||||
|
number = number[1:]
|
||||||
|
|
||||||
|
if (prefix_len + postfix_len) >= len(number):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Сравниваем начало номера
|
||||||
|
if number[:prefix_len] != prefix:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Сравниваем конец номера
|
||||||
|
if number[-postfix_len:] != postfix:
|
||||||
|
return
|
||||||
|
|
||||||
|
return number[prefix_len:-postfix_len]
|
||||||
|
|
||||||
|
|
||||||
|
def sjson_dumps(*args, **kwargs):
|
||||||
|
kwargs['ensure_ascii'] = False
|
||||||
|
kwargs['separators'] = (',', ':')
|
||||||
|
|
||||||
|
return json.dumps(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
HTTP_COOKIE_ARGS = [
|
||||||
|
'version', 'name', 'value',
|
||||||
|
'port', 'port_specified',
|
||||||
|
'domain', 'domain_specified',
|
||||||
|
'domain_initial_dot',
|
||||||
|
'path', 'path_specified',
|
||||||
|
'secure', 'expires', 'discard', 'comment', 'comment_url', 'rest', 'rfc2109'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def cookie_to_dict(cookie):
|
||||||
|
cookie_dict = {
|
||||||
|
k: v for k, v in cookie.__dict__.items() if k in HTTP_COOKIE_ARGS
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie_dict['rest'] = cookie._rest
|
||||||
|
cookie_dict['expires'] = None
|
||||||
|
|
||||||
|
return cookie_dict
|
||||||
|
|
||||||
|
|
||||||
|
def cookie_from_dict(d):
|
||||||
|
return Cookie(**d)
|
||||||
|
|
||||||
|
|
||||||
|
def cookies_to_list(cookies):
|
||||||
|
return [cookie_to_dict(cookie) for cookie in cookies]
|
||||||
|
|
||||||
|
|
||||||
|
def set_cookies_from_list(cookie_jar, l):
|
||||||
|
for cookie in l:
|
||||||
|
cookie_jar.set_cookie(cookie_from_dict(cookie))
|
||||||
|
|
||||||
|
|
||||||
|
def enable_debug_mode(vk_session, print_content=False):
|
||||||
|
""" Включает режим отладки:
|
||||||
|
- Вывод сообщений лога
|
||||||
|
- Вывод http запросов
|
||||||
|
|
||||||
|
:param vk_session: объект VkApi
|
||||||
|
:param print_content: печатать ответ http запросов
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from . import __version__
|
||||||
|
|
||||||
|
pypi_version = requests.get(
|
||||||
|
'https://pypi.org/pypi/vk_api/json'
|
||||||
|
).json()['info']['version']
|
||||||
|
|
||||||
|
if __version__ != pypi_version:
|
||||||
|
print()
|
||||||
|
print('######### MODULE IS NOT UPDATED!!1 ##########')
|
||||||
|
print()
|
||||||
|
print('Installed vk_api version is:', __version__)
|
||||||
|
print('PyPI vk_api version is:', pypi_version)
|
||||||
|
print()
|
||||||
|
print('######### MODULE IS NOT UPDATED!!1 ##########')
|
||||||
|
print()
|
||||||
|
|
||||||
|
class DebugHTTPAdapter(requests.adapters.HTTPAdapter):
|
||||||
|
def send(self, request, **kwargs):
|
||||||
|
start = time.time()
|
||||||
|
response = super(DebugHTTPAdapter, self).send(request, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
|
||||||
|
total = end - start
|
||||||
|
|
||||||
|
body = request.body
|
||||||
|
if body and len(body) > 1024:
|
||||||
|
body = body[:1024] + '[STRIPPED]'
|
||||||
|
|
||||||
|
print(
|
||||||
|
'{:0.2f} {} {} {} {} {} {}'.format(
|
||||||
|
total,
|
||||||
|
request.method,
|
||||||
|
request.url,
|
||||||
|
request.headers,
|
||||||
|
repr(body),
|
||||||
|
response.status_code,
|
||||||
|
response.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if print_content:
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
vk_session.http.mount('http://', DebugHTTPAdapter())
|
||||||
|
vk_session.http.mount('https://', DebugHTTPAdapter())
|
||||||
|
|
||||||
|
vk_session.logger.setLevel(logging.INFO)
|
||||||
|
vk_session.logger.addHandler(logging.StreamHandler(sys.stdout))
|
Loading…
Reference in new issue