Beispiel #1
0
def set_widget():
    service = VkApi(token=bot.get_settings("service_token"), api_version=bot.vk.api_version)
    widget = {
        "title": "Список лучших игроков клана",
        "title_url": "https://vk.com/kronos_bs",
        "more": "Перейти к полному списку",
        "more_url": "https://vk.com/topic-187742365_41459579",
        "head": [{
            "text": "Игрок",
            "align": "left"
        }, {
            "text": "Трофеи",
            "align": "right"
        }],
        "body": []
    }
    members = brawl.get_club_members("#R020J99L", limit=11).response["items"]
    for k in range(0, len(members)):
        widget["body"].append([{
            "text": "{}. {}".format(k+1, members[k]["name"])
        }, {
            "text": bot.get_messages("widget_member", t=members[k]["trophies"])
        }])
    widget["title_counter"] = len(members)
    
    try:
        service.method("appWidgets.update", {
            "type": "table",
            "code": "return " + dumps(widget,  ensure_ascii=False, separators=(",", ":")) + ";"
        })
    except:
        bot.log_exception()
Beispiel #2
0
class Bot:
    """Бот"""
    def __init__(self, token, id, db):
        super().__init__()
        self.token = token
        self.id = id
        self.db = db

        self.session = VkApi(token=token, api_version="5.124")
        self.vk = self.session.get_api()
        #self.longpoll = VkBotLongPoll(self.session, group_id=id)

        self.keyboards = None

    def setKeyboards(self, keyboards):
        self.keyboards = keyboards

    def newUser(self, event):
        self.sendKeyboard(event.object.user_id, "main_keyboard", "Добро пожаловать!")

    def userExit(self, event):
        #print(f"Пользователь {event.object.user_id} запретил сообщения.")
        pass

    def writeMsg(self, user_id, message):
        """Отправить пользователю сообщение"""
        self.session.method('messages.send', {'user_id': user_id, 'message': message, "random_id":get_random_id()})

    def attachmentMsg(self, user_id, attachment_type, attachment_id):
        """Отправить пользователю изображение"""
        ownid = f"-{self.id}"
        self.session.method('messages.send', {'user_id': user_id, "random_id":get_random_id(), "attachment":f"{attachment_type}{ownid}_{attachment_id}"})

    def repostPost(self, user_id, id):
        """Отправить пользователю запись"""
        self.attachmentMsg(user_id, "wall", id)

    def setCurrentKeyboard(self, id, keyboard):
        """Установить клавиатуру в бд"""
        self.db.update("Students", "current_keyboard", f"'{keyboard}'", f"WHERE user_id='{id}'")
        self.db.connection.commit()

    def sendKeyboard(self, from_id, keyboard, text="", set_as_current=False, static=False):
        """Отправить пользователю клавиатуру"""
        if keyboard in self.keyboards:
            if text == "": text = "Выполнено"
            if set_as_current:
                self.setCurrentKeyboard(from_id, keyboard)
            if static:
                keyboard=self.keyboards[keyboard].build(from_id)
                keyboard = keyboard.get_keyboard()
            else:
                keyboard = self.keyboards[keyboard].keyboard.get_keyboard()
            self.vk.messages.send(
                user_id=from_id,
                random_id=get_random_id(),
                peer_id=from_id,
                keyboard=keyboard,
                message=text
            )
Beispiel #3
0
class VkAPI:

    def __init__(self, token):
        self._api = VkApi(token=token)
        self._longpoll = VkLongPoll(self._api)

    def write_msg(self, user_id, s):
        self._api.method('messages.send', {'user_id': user_id, 'message': s})

    def get_incoming_events(self):
        return (e for e in self._longpoll.listen() if e.to_me)
Beispiel #4
0
def _send_startup_event(group_id, group_secret):
    account = random.choice(tech_accounts)
    sess = VkApi(token=account['access_token'])
    payload = {'group_id': abs(group_id)}
    resp = sess.method('messages.allowMessagesFromGroup', payload)

    data_json = {
        "type": "message_new",
        "object": {
            "date": int(time.time()),
            "from_id": account['id'],
            "id": 777,
            "out": 0,
            "peer_id": account['id'],
            "text": "/startup",
            "conversation_message_id": 777,
            "fwd_messages": [],
            "important": False,
            "random_id": 0,
            "attachments": [],
            "is_hidden": False
        },
        "group_id": abs(group_id),
        "secret": group_secret
    }
    data = json.dumps(data_json)
    req = requests.post(DJANGO_URL, data=data)
    return 1
Beispiel #5
0
class BaseVkBot:
    token = ''
    api_v = ''
    vk = None
    longPoll = None

    def __init__(self):
        self.loadEnv()

        self.token = environ['token']
        self.api_v = environ['api_v']

        self.vk = VkApi(token=self.token)
        self.longPoll = longpoll.VkLongPoll(self.vk)

    def run(self):
        for event in self.longPoll.listen():
            if event.type == longpoll.VkEventType.MESSAGE_NEW and event.to_me:
                self.newMessage({
                    'user_id': event.user_id,
                    'message': event.text
                })

    def loadEnv(self):
        try:
            file = open('.env', 'r')

            for line in file:
                line = line.rstrip()

                temp = line.split('=')

                environ[temp[0]] = temp[1]
        except FileNotFoundError as e:
            print('File .env not exists')
            exit()

    def sendMessage(self, user_id, message):
        random_id = randint(1, 10000000)
        self.vk.method('messages.send', {
            'user_id': user_id,
            'message': message,
            'random_id': random_id
        })

    def newMessage(self, event):
        pass
Beispiel #6
0
class VK:
    def __init__(self, token=None):
        self.vk = VkApi(token=token)
        self.log = getLogger('VK')
        try:
            self.who_am_i()
        except ApiError:
            self.log.fatal('Invalid VK token passed')
            exit(-1)

    def get_conversations(self):
        result = []
        offset = 0
        while True:
            data = self.vk.method('messages.getConversations',
                                  {'offset': offset, 'count': 200, 'extended': 0})
            if not data['items']:
                break
            data['items'] = [x for x in data['items'] if x['conversation']['peer']['type'] == 'user' and x['conversation']['peer']['id'] != 100]
            sleep(0.4)
            offset += 200

            result.append(data)
        return result

    def get_messages(self, conversation, offset=0):
        while True:
            data = self.vk.method('messages.getHistory', {
                'offset': offset,
                'count': 200,
                'user_id': conversation['conversation']['peer']['id'],
                'extended': 0
            })
            offset += 200
            if not data['items']:
                break

            yield data, offset

    def who_am_i(self):
        return self.vk.method('users.get')[0]

    def get_sns(self, ids: list) -> tuple:
        ids = [str(x) for x in ids]
        return tuple([(x.get('first_name', None), x.get('last_name', None)) if x.get('first_name') and x.get('last_name') else ('Удаленный', 'пользователь') for x in self.vk.method('users.get', {'user_ids': ','.join(ids)})])
Beispiel #7
0
def set_topic():
    user = VkApi(token=bot.get_settings("admin_token"), api_version=bot.vk.api_version)
    members = brawl.get_club_members("#R020J99L").response["items"]
    msg = "Список участников [{}/100]\n".format(len(members))
    for k in range(0, len(members)):
        msg += bot.get_messages("member",
            role=get_role(members[k]["role"]),
            t=members[k]["trophies"],
            name=members[k]["name"],
            tag=members[k]["tag"],
            n=k+1
            )
        msg += "\n"
        
    try:
        user.method("board.editComment", {
            "group_id": bot.lp.group_id,
            "comment_id": 2,
            "topic_id": 41459579,
            "message": msg
            })
    except:
        bot.log_exception()
def load_avatars():
    login, password = json.load(open('secret.json'))
    vk = VkApi(login, password)
    vk.auth()

    target = 'annndruha'

    members = []
    try:
        id = int(target.replace('id', ''))
    except:
        id = vk.method('users.get', {'user_ids': target})[0]['id']

    members = vk.method('friends.get', {'user_id': id})["items"]

    print(len(members))
    #print(members)

    members_str = ','.join(map(str, members))
    members_data = vk.method('users.get', {
        'user_ids': members_str,
        'fields': 'photo_400_orig'
    })

    for data in members_data:
        try:
            url = data['photo_400_orig']
            id = data['id']
            filename = f'avatars/{id}.jpg'

            response = requests.get(url)
            if response.status_code == 200:
                with open(filename, 'wb') as imgfile:
                    imgfile.write(response.content)
        except:
            pass
Beispiel #9
0
    def initializeTracksCount(self):
        session = VkApi(self.authScreen.loginInput.text,
                        self.authScreen.passInput.text,
                        auth_handler=self.authHandler,
                        captcha_handler=self.captchaHandler)

        try:
            session.auth(reauth=True)
        except Exception as ex:
            self.screenManagement.transition.direction = "left"
            self.screenManagement.current = "auth"

            print(ex)

            return

        audio = vkapi.audio(session)
        self.tracksCount = audio.get_len_user_audio()

        if self.tracksCount != 0:
            self.startScreen.tracksCount.label = "Аудиозаписей: " + str(
                self.tracksCount)
        else:
            self.startScreen.tracksCount.label = "Нет аудиозаписей"

        user = session.method("users.get", {
            "user_ids": audio.user_id,
            "fields": "photo_200"
        })

        self.startScreen.username.text = user[0]["first_name"] + ' ' + user[0][
            "last_name"]
        self.startScreen.avatar.source = user[0]["photo_200"]

        self.screenManagement.transition.direction = "left"
        self.screenManagement.current = "start"
Beispiel #10
0
class GetAudioListThread(QThread):
    signal = pyqtSignal("PyQt_PyObject")
    str_signal = pyqtSignal(str)
    image_signal = pyqtSignal("QImage")

    def __init__(self, cookie, window):
        QThread.__init__(self)
        self.login = ""
        self.password = ""
        self.user_link = ""
        self.statusBar = None
        self.save_password = False
        self.authorized = False
        self.cookie = cookie
        self.window = window

    def __del__(self):
        self.wait()

    def _user_auth(self):
        if self.login:
            self.session = VkApi(
                login=self.login,
                password=self.password,
                auth_handler=self.auth_handler,
                captcha_handler=self.captcha_handler,
                config_filename=self.cookie,
            )
            self.statusBar.showMessage("Авторизация.")
            self.session.auth()
        else:
            self.statusBar.showMessage(
                "Логин не указан, использование пароля в качестве токена")
            self.session = VkApi(token=self.password,
                                 captcha_handler=self.captcha_handler)
        self.vk_audio = VkAudio(self.session)
        self.authorized = True

    def _get_audio(self):
        tracks = []
        albums = []
        string = str()
        # Try to get post audio list
        post = self.get_group_and_post_id(self.user_link)
        album = self.get_album_id(self.user_link)
        if isinstance(post, tuple):
            owner_id, post_id = post
            self.statusBar.showMessage("Получение списка аудиозаписей поста.")
            string = "Аудиозаписи поста"
            tracks = self.vk_audio.get_post_audio(owner_id, post_id)
            audios = ",".join(["{owner_id}_{id}".format(**i) for i in tracks])
            tracks = self.session.method(method="audio.getById",
                                         values={"audios": audios})
        elif isinstance(album, tuple):
            owner_id, album_id, *_ = album
            self.statusBar.showMessage(
                "Получение списка аудиозаписей альбома.")
            string = "Аудиозаписи альбома"
            tracks = self._get_tracks(owner_id, album_id)
        else:
            user_id = self.get_user_id(self.user_link)
            # Try to get user or group audio list
            # noinspection PyBroadException
            try:
                owner_id = self.session.method("users.get",
                                               dict(user_ids=user_id))[0]
                self.statusBar.showMessage(
                    "Получение списка аудиозаписей пользователя: {first_name} {last_name}"
                    .format(**owner_id))
                string = "Музыка пользователя: {first_name} {last_name}".format(
                    **owner_id)
            except Exception:
                group_id = self.session.method("groups.getById",
                                               dict(group_id=user_id))[0]
                self.statusBar.showMessage(
                    "Получение списка аудиозаписей сообщества: {name}".format(
                        **group_id))
                string = "Музыка сообщества: {}".format(group_id["name"])
                albums = self._get_albums(-group_id["id"])
                tracks = self._get_tracks(-group_id["id"])
            else:
                albums = self._get_albums(owner_id["id"])
                tracks = self._get_tracks(owner_id["id"])
        for album in albums:
            try:
                album["tracks"] = self.vk_audio.get(
                    owner_id=album["owner_id"],
                    album_id=album["id"],
                    access_hash=album["access_hash"],
                )
            except:
                album["tracks"] = self._get_tracks(owner_id["id"], album["id"])
        return tracks, string, albums

    def _get_tracks(self, owner_id, album_id=None, access_hash=None):
        try:
            tracks = self.vk_audio.get(owner_id, album_id, access_hash)
        except:
            values = {"owner_id": owner_id}
            if album_id:
                values.update({"album_id": album_id})
            res = self.session.method(
                method="audio.get",
                values=values,
            )
            count = res["count"]
            offset = 0
            tracks = []
            while count != 0:
                audios = ",".join(
                    ["{owner_id}_{id}".format(**i) for i in res["items"]])
                tracks.extend(
                    self.session.method(method="audio.getById",
                                        values={"audios": audios}))
                offset += 200 if count >= 200 else count % 200
                count -= 200 if count >= 200 else count % 200
                values.update({"offset": offset})
                res = self.session.method(
                    method="audio.get",
                    values=values,
                )
        return tracks

    def _get_albums(self, owner_id):
        try:
            albums = self.vk_audio.get_albums(owner_id["id"])
        except:
            res = self.session.method(
                method="audio.getPlaylists",
                values={"owner_id": owner_id},
            )
            count = res["count"]
            offset = 0
            albums = []
            while count != 0:
                albums.extend(res["items"])
                offset += 10 if count >= 10 else count % 10
                count -= 10 if count >= 10 else count % 10
                res = self.session.method(
                    method="audio.getPlaylists",
                    values={
                        "owner_id": owner_id,
                        "offset": offset
                    },
                )
        return albums

    def auth_handler(self):
        """
        При двухфакторной аутентификации вызывается эта функция.
        :return: key, remember_device
        """
        self.str_signal.emit("Введите код авторизации:")
        while not self.window.key:
            pass
        return self.window.key, self.save_password

    def captcha_handler(self, captcha):
        url = captcha.get_url()
        file = TemporaryFile()
        res = self.session.http.get(url, stream=True)
        res.raw.decode_content = True
        shutil.copyfileobj(res.raw, file)
        file.seek(0)
        image = QImage()
        image.loadFromData(file.read())
        self.image_signal.emit(image)
        while not self.window.key:
            pass
        return captcha.try_again(self.window.key)

    def run(self):
        try:
            if not self.authorized:
                self._user_auth()
            result = self._get_audio()
            self.signal.emit(result)
        except exceptions.BadPassword:
            self.signal.emit("Неверный логин или пароль.")
        except exceptions.LoginRequired:
            self.signal.emit("Требуется логин.")
        except exceptions.PasswordRequired:
            self.signal.emit("Требуется пароль.")
        except (IndexError, AttributeError):
            self.signal.emit(
                "Невозможно получить список аудиозаписей. Проверьте, открыты ли они у пользователя."
            )
        except exceptions.ApiError as e:
            if "113" in str(e):
                self.signal.emit(
                    "Неверная ссылка на профиль пользователя (неверный ID пользователя)."
                )
            elif "100" in str(e):
                self.signal.emit(
                    "Неверная ссылка на профиль пользователя (сообщества).")
            else:
                self.signal.emit(str(e))
        except Exception as e:
            self.signal.emit(str(type(e)) + str(e))

    @staticmethod
    def get_user_id(link):
        result = findall(r"(https?://m?\.?vk\.com/)?(.*)$", link)[0][1]
        return result if result else None

    @staticmethod
    def get_group_and_post_id(link):
        result = findall(r"wall(.*?)_(.*?)$", link)
        return result[0] if result else None

    @staticmethod
    def get_album_id(link):
        link = link.replace("%2F", "/")
        result = findall(r"playlist/(.*)_(.*)_(.*)\?", link)
        if not result:
            result = findall(r"playlist/(.*)_(.*)_(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)&access_hash=(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)/(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)", link)
        return result[0] if result else None
Beispiel #11
0
def group_add(access_token, CurrentUse=None):
    # todo get latest chat id
    need_perms = ['photos', 'docs', 'messages', 'manage', 'wall']
    sess = VkApi(token=access_token)

    try:
        perms = sess.method('groups.getTokenPermissions')
    except:
        return 0, 'Неверный токен'

    have_perms = set()
    for perm in perms['permissions']:
        have_perms.add(perm['name'])

    for perm in need_perms:
        if perm not in have_perms:
            return 0, 'Необходимые права токена: ' + ', '.join(need_perms)

    group = sess.method('groups.getById', {'fields': 'description'})[0]
    group_id = group['id']
    try:
        group_obj = ConnectedGroup.objects.get(id=-group_id)
        return 0, 'Эта группа уже есть в базе данных'
    except ObjectDoesNotExist:
        pass

    payload = {
        'group_id': group_id,
        'enabled': 1,
        'api_version': 5.87,
        'message_new': 1,
        'group_change_settings': 1,
    }
    # todo all to 0?
    try:
        result = sess.method('groups.setLongPollSettings', payload)
    except:
        result = 0
    if not result:
        return 0, 'Не удалось изменить настройки Long Poll сервера'

    payload = {
        'group_id': group_id,
        'messages': 1,
        'bots_add_to_chat': 1,
        'bots_capabilities': 1,
        'bots_start_button': 1,
        'v': 5.101
    }
    try:
        result = sess.method('groups.setSettings', payload)
    except:
        result = 0
    if not result:
        return 0, 'Не удалось включить функции бота'
    secret = randomString(16)
    owner = CurrentUse.user
    group_obj = ConnectedGroup(id=-group_id,
                               access_token=access_token,
                               secret=secret,
                               owner=owner)
    group_obj.save()
    time.sleep(1)
    r = _send_startup_event(group_id, secret)

    notify('Добавлена новая группа\nhttps://vk.com/club{}'.format(
        abs(group_id)))
    return 1, '1'
Beispiel #12
0
class Vk:
    session = None
    long_pool = None

    def __init__(self, token):
        self.session = VkApi(token=token)
        self.long_pool = VkLongPoll(self.session)

    def send(self,
             id,
             text,
             buttons=None,
             forward_messages=None,
             attachments=None):
        if type(text) is list:
            text = text[random.randint(0, len(text) - 1)]
        message = {
            'user_id': id,
            'message': text,
            'forward_messages': forward_messages,
            'attachment': attachments,
            'random_id': random.randint(0, sys.maxsize * sys.maxsize * 2)
        }
        if buttons or buttons == []:
            message.update({
                'keyboard':
                str(json.dumps({
                    'one_time': False,
                    'buttons': buttons
                }))
            })
        self.session.method('messages.send', message)

    def send_sticker(self, id, num):
        self.session.method(
            'messages.send', {
                'user_id': id,
                'sticker_id': num,
                'random_id': random.randint(0, sys.maxsize * sys.maxsize * 2)
            })

    def send_message_sticker(self, id, text, sticker_num):
        self.send(id, text)
        self.send_sticker(id, sticker_num)

    def get_unread_conversations(self, offset=0, count=200):
        conversations = self.session.method('messages.getConversations', {
            'filter': 'unread',
            'offset': offset,
            'count': count,
            'extended': 0
        })
        return [
            conversation for conversation in conversations['items']
            if conversation['conversation']['peer']['type'] != 'chat'
        ], conversations['count']

    def get_message_attachments(self, message_id):
        attachments = self.session.method('messages.getById',
                                          {'message_ids': message_id})
        return attachments['items'][0][
            'attachments'] if attachments['count'] != 0 else None

    def get_users_names(self, user_ids):
        try:
            users = self.session.method('users.get', {'user_ids': user_ids})
            return [
                f'{user["first_name"]} {user["last_name"]}' for user in users
            ]
        except:
            return None

    @staticmethod
    def is_photo(event):
        return event.attachments and event.attachments.get(
            'attach1_type') == MediaTypes.photo

    @staticmethod
    def is_audio_msg(event):
        return event.attachments and event.attachments.get(
            'attach1_kind') == MediaTypes.audiomsg

    @staticmethod
    def is_audio(event):
        return event.attachments and event.attachments.get(
            'attach1_type') == MediaTypes.audio

    @staticmethod
    def is_video(event):
        return event.attachments and event.attachments.get(
            'attach1_type') == MediaTypes.video
Beispiel #13
0

def splitJson(array):
    js = {}
    for i in range(0, len(array) - 1):
        js.update({array[i]: array[i + 1]})
    return js


f = open('config.conf')
data = f.read().replace(' ', '').split('\n')
f.close()
confLog = splitJson(data[0].split(':'))
configPass = splitJson(data[1].split(':'))
confLog.update(configPass)

selector = input("Selector: ")
ids = input("id: ")
file = input("File: ")
from vk_api import VkApi
vk = VkApi(login=confLog['login'], password=confLog['password'])
vk.auth()
token = vk.token['access_token']

dray = lib.UploadVoiceMsg(token, "voices/" + file)
dray.upload()
vk.method("messages.send", {
    selector: ids,
    'attachment': dray.returnAttachment()
})
print("all is okay")
            'payload': payload,
            'callback': None
        }
        response.append(cur_action)
    if agr_level in agr_handlers and agr_handlers[agr_level]:
        response = agr_handlers[agr_level](response)

    return response


try:
    sess = VkApi(token=VK_MY_TOKEN)
    exec_stack = ExecuteStack(sess)
    payload = {'user_ids': uid, 'fields': 'online'}

    is_online = sess.method('users.get', payload)[0]['online']
    if is_online and (time.time() - storage['latest_send']) > SEND_TIMEOUT:
        storage['latest_send'] = time.time()
        actions = gen_actions()
        for action in actions:
            exec_stack.add(action['method'],
                           action['payload'],
                           additional=action['callback'])
        resps = exec_stack.finish()
        for resp, callback in resps:
            if callback:
                callback(resp)

        storage['current_notifies'] += 1
        if storage['current_notifies'] == 1:
            storage['current_first'] = time.time()
class GetInfo:
    def __init__(self, login, password, target, type = 'group_members', normed = False):
        self.vk = VkApi(login, password)
        self.vk.auth()

        self.target = target
        self.type = type
        self.normed = normed

        self.members = []
        self.bdate_mans = []
        self.bdate_womans = []
        self.count_of_members = 0
        self.mens = 0
        self.womans =0
        self.no_sex = 0
        self.deleted = 0
        self.banned = 0
        self.active = 0
        self.open_accounts=0
        self.closed_accounts=0
        self.avg_mens = 0
        self.avg_womans = 0


    def get_members_ids(self):
        offset = 0
        if not os.path.exists(str(self.target)):
            os.makedirs(str(self.target))

        try:
            if self.type == 'group_members':
                self.count_of_members = self.vk.method('groups.getMembers', {'group_id':self.target, 'count':1})['count']
                start_time = time.time()
                while len(self.members)<self.count_of_members:
                    _persent = int(100*len(self.members)/self.count_of_members)
                    _mem = f'{len(self.members)}/{self.count_of_members}'
                    _time = "%.1f" % (time.time()-start_time)
                    _time_left = "%.1f" % (self.count_of_members*(time.time()-start_time)/(len(self.members)+1)-(time.time()-start_time))
                    print(f'Geting ids... {_persent}% ({_mem})\t time: {_time} \t left: {_time_left}')
                    self.members += self.vk.method('execute',
                        {'code':
                            '''var members = [];
                            var offset = %i; 
                            var i = 0;
                            var members_count = %i;

                            while ((i<25) &&(offset<members_count))
                            {
                            members = members + API.groups.getMembers({"group_id":"%s", "offset":offset })["items"];
                            i = i + 1;
                            offset = offset + 1000;
                            };
                            return members;''' % (offset, self.count_of_members, self.target)
                        })
                    offset +=25000

            elif self.type == 'user_friends':
                print(f'Get friens ids...')
                try:
                    id = int(self.target.replace('id',''))
                except:
                    id = self.vk.method('users.get', {'user_ids':self.target})[0]['id']
                else:
                    self.vk.method('friends.get', {'user_id':id})

                self.members +=self.vk.method('friends.get', {'user_id':id})["items"]
                self.count_of_members = len(self.members)
                print(f'Users: {self.count_of_members}')

            print('Get ids done. Saving...')
        except Exception as err:
            print(f'Exception: {str(err)}')

        with open(f'{self.target}/{self.target}_ids.json', 'w') as outfile:
            json.dump(self.members, outfile)
        print('Ids saved.')
        return None

    def get_users_data(self):
        print('Ids loading...')
        members_ids = json.load(open(f'{self.target}/{self.target}_ids.json'))
        print('Ids loaded. Start getting data...')

        try:
            offset = 0
            members_data = []
            start_time = time.time()
            while offset < len(members_ids):

                members = members_ids[offset:offset+1000]
                members_str = ','.join(map(str, members))
                members_data += self.vk.method('users.get', {'user_ids':members_str, 'fields':'bdate, sex'})
                offset += 1000

                _persent = int(100*len(members_data)/len(members_ids))
                _mem = f'{len(members_data)}/{len(members_ids)}'
                _time = "%.1f" % (time.time()-start_time)
                _time_left = "%.1f" % (len(members_ids)*(time.time()-start_time)/(len(members_data))-(time.time()-start_time))
                print(f'Geting data... {_persent}% ({_mem})\t time: {_time} \t left: {_time_left}')

            print('Get users data done. Saving...')
        except Exception as err:
            print(f'Exception: {str(err)}')

        with open(f'{self.target}/{self.target}_data.json', 'w') as outfile:
            json.dump(members_data, outfile)
        print('Users data saved.')
        return None

    def calculate(self):
        print('Users data loading...')
        members_data = json.load(open(f'{self.target}/{self.target}_data.json'))
        self.count_of_members = len(members_data)
        print('Users data loaded.')

        print('Calculate...')
        for data in members_data:
            if 'deactivated' in data:
                if data['deactivated']=='deleted':self.deleted+=1
                else: self.banned+=1
                continue

            self.active += 1
            if data['is_closed']: self.closed_accounts +=1
            else: self.open_accounts +=1
            
            if data['sex']==1: self.womans += 1
            elif data['sex']==2: self.mens += 1
            else: self.no_sex +=1   

            if (('bdate' in data) and (len(data['bdate'].split('.'))==3)): # Если дата указана и содержит год рожения
                year = int((data['bdate'].split('.'))[2])
                if year>1960: # Хвост графика из фейковых дат рождения нас не интересует
                    if data['sex']==1: self.bdate_womans.append(year)
                    elif data['sex']==2: self.bdate_mans.append(year)
                        
        print('Calculate done.')
        return None

    def make_plot(self):
        print('Plotting...')
        bdate_mans = np.array(self.bdate_mans)
        bdate_womans = np.array(self.bdate_womans)
        self.avg_mens = np.average(bdate_mans)
        self.avg_womans = np.average(bdate_womans)
        bins = np.arange(min(min(bdate_womans),min(bdate_mans)), max(max(bdate_womans),max(bdate_mans))+1, 1)

        fig, ax1 = plt.subplots()
        fig.set_size_inches((16, 9), forward=False)

        if self.normed: # Нормируется соотношение полов, основываясь на общем соотношении, независимо от числа открытых женских аккаунтов
            norm_koeff = (self.mens/self.womans)/(len(bdate_mans)/len(bdate_womans))
            bincount_m = np.bincount(bdate_mans)[1961:]*norm_koeff
            bincount_w = np.bincount(bdate_womans)[1961:]

        else:
            bincount_m = np.bincount(bdate_mans)[1961:]
            bincount_w = np.bincount(bdate_womans)[1961:]
            if len(bincount_m)<len(bincount_w):
                n = len(bincount_w)-len(bincount_m)
                bincount_m = np.pad(bincount_m, [(0,n)], mode='constant')
            else:
                n = len(bincount_m)-len(bincount_w)
                bincount_w = np.pad(bincount_w, [(0,n)], mode='constant')

        try:
            width = 0.4
            ax1.bar(bins- width/2, bincount_w, width, label = 'W', color = ['violet'])
            ax1.bar(bins+ width/2, bincount_m, width, label = 'M', color = ['slateblue'])
        except:
            bdates = np.array([bdate_womans, bdate_mans])
            ax1.hist(bdates, bins, histtype='bar', align='left', color = ['violet','slateblue'])

        labels = [str(i) for i in bins]
        ax1.set_xticks(bins)
        ax1.set_xticklabels(labels, rotation=90)
        ax1.set_xlabel("Год рождения")
        ax1.set_ylabel("Количество человек")
        

        labels_top = [str(2020-i) for i in bins]
        ax2 = ax1.twiny()
        ax2.set_xlim(ax1.get_xlim())
        ax2.set_xticks(bins)
        ax2.set_xticklabels(labels_top, rotation=90)
        ax2.set_xlabel("Возраст")

        try:
            if self.type == 'group_members':
                group_info = self.vk.method('groups.getById', {'group_ids':self.target})
                plt.title(f'''"{group_info[0]["name"]}" - Возрастно-половая диаграмма''')
            else:
                user_info = self.vk.method('users.get', {'user_ids':self.target})[0]
                first_name, last_name = user_info['first_name'], user_info['last_name']
                plt.title(f'Возрастно-половая диаграмма друзей: {first_name} {last_name}')
        except:
            plt.title(f'Возрастно-половая диаграмма: {self.target}')
        
        a = mpatches.Patch(color='slateblue', label=f'Мужчин: {self.mens} ({int(100*(self.mens/(self.mens+self.womans)))}%)')
        b = mpatches.Patch(color='violet', label=f'Женщин: {self.womans} ({int(100*(self.womans/(self.mens+self.womans)))}%)')

        c = mpatches.Patch(color='white', label ='')
        d = mpatches.Patch(color='gray', label=f'Активных: {self.active} ({int(100*(self.active/(self.banned+self.active+self.deleted)))}%)')
        e = mpatches.Patch(color='gray', label=f'Активных закрытых: {self.closed_accounts} ({int(100*(self.closed_accounts/(self.open_accounts+self.closed_accounts)))}%)')
        f = mpatches.Patch(color='gray', label=f'Активных открытых: {self.open_accounts} ({int(100*(self.open_accounts/(self.open_accounts+self.closed_accounts)))}%)')
        g = mpatches.Patch(color='gray', label=f'Удалённых: {self.deleted} ({int(100*(self.deleted/(self.banned+self.active+self.deleted)))}%)')
        h = mpatches.Patch(color='gray', label=f'Заблокированных: {self.banned} ({int(100*(self.banned/(self.banned+self.active+self.deleted)))}%)')
        i = mpatches.Patch(color='gray', label=f'Всего: {self.count_of_members}')

        j = mpatches.Patch(color='white', label ='')
        k = mpatches.Patch(color='gray', label=f'Указан год и пол: {len(bdate_womans)+len(bdate_mans)} ({int(100*((len(bdate_womans)+len(bdate_mans))/self.count_of_members))}%)')
        l = mpatches.Patch(color='gray', label=f'из них: М: {len(bdate_mans)} ({int(100*(len(bdate_mans)/(len(bdate_womans)+len(bdate_mans))))}%) Ж: {len(bdate_womans)} ({int(100*(len(bdate_womans)/(len(bdate_womans)+len(bdate_mans))))}%)')
        m = mpatches.Patch(color='gray', label =f'Нормировка графика по полу: {self.normed}')
        n = mpatches.Patch(color='gray', label =f'Ср. возраст мужчин: {2020-int(self.avg_mens)}')
        o = mpatches.Patch(color='gray', label =f'Ср. возраст женщин: {2020-int(self.avg_womans)}')


        plt.legend(handles=[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o], loc='upper left')

        plt.savefig(f'{self.target}/{self.target}.png', dpi=320, bbox_inches='tight')
        print(f'Plot save in {self.target}/{self.target}.png')
        return None
Beispiel #16
0
class VkBot(IBot):
    def __init__(self, token: str, group_id: int):
        super().__init__()
        self._api = VkApi(token=token)
        self._longpoll = VkBotLongPoll(self._api, group_id=group_id)
        self._longpoll.wait = 0
        self._current_user: Union[int, None] = None

    def change_user(self, user_id: int):
        self._current_user = user_id
        self.on_new_user.notify(user_id)

    def send_message(self, message: Message):
        if self._current_user is None:
            return
        self._api.method('messages.send',
                         values={
                             'peer_id': self._current_user,
                             'message': message.text,
                             'random_id': int(getrandbits(32))
                         })

    def set_keyboard(self, keyboard: Keyboard, message: str = 'keyboard'):
        vk_keyboard = VkKeyboard(True)
        current_line = 1
        for pos, button in keyboard.all():
            row, column = pos
            if row + 1 < current_line:
                current_line += 1
                vk_keyboard.add_line()
            vk_keyboard.add_button(button.text, VK_BUTTON_COLOR[button.color])
        self._api.method('messages.send',
                         values={
                             'peer_id': self._current_user,
                             'random_id': int(getrandbits(32)),
                             'keyboard': vk_keyboard.get_keyboard(),
                             'message': message
                         })

    def get_current_user(self):
        return self._current_user

    def handle_server_event(self):
        last_notification_id = int(cfg.get('SETTINGS', 'LAST_ID'))
        data = requests.get(
            cfg.get('API', 'API_ADDRESS') +
            f'/notifications/{last_notification_id}').json()
        last_notification_id = data['meta']['last_id']
        cfg.set('SETTINGS', 'LAST_ID', str(last_notification_id))
        cfg.write(open('settings.cfg', 'w'))
        for student, notifications in data['students'].items():
            self.change_user(int(student))
            for student_data in notifications:
                current_contest = None
                for contest, contest_data in data['contests'].items():
                    if contest == str(student_data['contest']):
                        current_contest = contest_data
                        current_contest['id'] = contest
                        break
                current_stage = None
                for stage, stage_data in data['stages'].items():
                    if stage == str(student_data['stage']):
                        current_stage = stage_data
                        current_stage['id'] = stage
                        break
                self.on_event.notify({
                    'contest': current_contest,
                    'stage': current_stage,
                    'student': student
                })

    def run(self) -> NoReturn:
        print('Бот начал работу!')
        schedule.every(5).seconds.do(self.handle_server_event)
        while True:
            try:
                for event in self._longpoll.check():
                    if event.type == VkBotEventType.MESSAGE_NEW and event.from_user:
                        self.change_user(event.message.from_id)
                        self.on_new_message.notify(
                            Message(event.message.get('text', '')))
                schedule.run_pending()
            except requests.ConnectionError:
                print(f'{datetime.now()}: Не удается подключиться к серверу!')
Beispiel #17
0
            else:
                send('Неверно. Попытка удаления акаунта была заблокирована.')
                database_query(f'UPDATE users SET state=1 WHERE user_id={user_id}')

        elif state == 4:  # состояние блокировки
            send('❌ Ваш акаунт был заблокирован.')

        elif state == 5:  # состояние отправки сообщения
            send('✅ Отправлено;')

            recipient = database.execute(f'SELECT viewed_user FROM users WHERE user_id={user_id}').fetchone()
            recipient = database.execute(
                f'SELECT name, surname, user_id FROM users WHERE user_id={recipient[0]}').fetchone()
            sender = database.execute(f'SELECT name, surname FROM users WHERE user_id={user_id}').fetchone()
            user = bot.method(f'users.get', {'user_ids': [user_id], 'fields': 'screen_name'})[0]
            try:
                send(f'@{user["screen_name"]} ({sender[0]} {sender[1]}) оставил тебе сообщение:\n'
                     f'{text}', None, recipient[2])

            except:
                send('Пользователь заблокировал бота.')

            database_query(f'UPDATE users SET state=1 WHERE user_id={user_id}')
            view()

        elif state == 7:  # состояние отправки реф. кода
            code = database.execute(f'SELECT user_id, name, surname FROM users WHERE referral_code="{text}"').fetchone()
            if code:
                if code[0] != user_id:
                    send(f'Реферальный код пользователя {code[1]} {code[2]} принят ✅\n')
Beispiel #18
0
class Server:

    def __init__(self, api_token, server_name: str = "Empty"):
        self.server_name = server_name
        self.vk = VkApi(token=api_token)
        self.long_poll = VkLongPoll(self.vk)
        self.vk_api = self.vk.get_api()
        self.event = None
        self.current_user_id = None
        # словарь подобранных людей
        self.users_info_dict = {}
        # счетчик просмотра подобранных людей
        self.count = 0
        # текущее состояние пользователя
        self.states = {}
        self.session = Session()

    def write_msg(self, message):
        self.vk.method('messages.send', {'user_id': self.event.user_id,
                                         'message': message,
                                         'random_id': randrange(10 ** 7)})

    def write_msg_keyboard(self, message, keyboard):
        self.vk.method('messages.send', {'user_id': self.event.user_id,
                                         'message': message,
                                         'random_id': randrange(10 ** 7),
                                         'keyboard': json.dumps(keyboards[keyboard], ensure_ascii=False)})

    def write_msg_attachment(self, message, attachment, keyboard):
        self.vk.method('messages.send', {'user_id': self.event.user_id,
                                         'message': message,
                                         'random_id': randrange(10 ** 7),
                                         'keyboard': json.dumps(keyboards[keyboard], ensure_ascii=False),
                                         'attachment': attachment})

    def hello_state(self, objects):
        if self.event.text == "выход" or self.event.text == "выйти":
            return False
        objects[0].state = "Initial"
        self.session.commit()
        self.write_msg_keyboard('Выберите действие:', 'greeting')

    def initial_state(self, objects):
        if self.event.text == "начать поиск":
            objects[0].state = "City"
            self.session.commit()
            self.write_msg('Введите город')
        elif self.event.text == "показать/удалить людей":
            orm.get_dating_list(self.event.user_id)
            orm.get_ignore_list(self.event.user_id)
            self.write_msg_keyboard('Привет! Выбери действие', 'like_ignore')
        elif self.event.text == "лайк":
            orm.dating_count = 0
            objects[0].state = "Like"
            self.session.commit()
            self.write_msg_keyboard(f'У вас найдено - {len(orm.dating_list)} человек. Приступим?', 'filter_msg')
        elif self.event.text == "игнор":
            orm.ignore_count = 0
            objects[0].state = "Ignore"
            self.session.commit()
            self.write_msg_keyboard(f'У вас найдено - {len(orm.ignore_list)} человек. Приступим?', 'filter_msg')
        elif self.event.text == "выйти":
            self.write_msg_keyboard('Выбери действие', 'greeting')
        else:
            self.write_msg_keyboard('Выбери действие', 'greeting')

    def like_state(self, objects):
        if self.event.text == "следующий":
            orm.dating_count += 1
            if orm.show_dating_user():
                dat_name, dat_age, dat_id, dat_attach = orm.show_dating_user()
                self.write_msg_attachment(f'{dat_name}\nВозраст - {dat_age}\nhttps://vk.com/id{dat_id}',
                                          f'photo{dat_id}_{dat_attach}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Список закончился. Выберите действие', 'greeting')
        elif self.event.text == "удалить":
            orm.dating_list[orm.dating_count].remove_dating_user()
            orm.dating_count += 1
            if orm.show_dating_user():
                dat_name, dat_age, dat_id, dat_attach = orm.show_dating_user()
                self.write_msg_attachment(f'{dat_name}\nВозраст - {dat_age}\nhttps://vk.com/id{dat_id}',
                                          f'photo{dat_id}_{dat_attach}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Список закончился. Выберите действие', 'greeting')
        elif self.event.text == "выйти":
            objects[0].state = "Initial"
            self.session.commit()
            self.write_msg_keyboard('Выбери действие', 'greeting')
        else:
            if orm.show_dating_user():
                dat_name, dat_age, dat_id, dat_attach = orm.show_dating_user()
                self.write_msg_attachment(f'{dat_name}\nВозраст - {dat_age}\nhttps://vk.com/id{dat_id}',
                                          f'photo{dat_id}_{dat_attach}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Лайк список пуст. Выберите действие', 'greeting')

    def ignore_state(self, objects):
        if self.event.text == "следующий":
            orm.ignore_count += 1
            if orm.show_ignore_user():
                self.write_msg_keyboard(f'https://vk.com/id{orm.show_ignore_user()}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Список закончился. Выберите действие', 'greeting')
        elif self.event.text == "удалить":
            orm.ignore_list[orm.ignore_count].remove_ignore_user()
            orm.ignore_count += 1
            if orm.show_ignore_user():
                self.write_msg_keyboard(f'https://vk.com/id{orm.show_ignore_user()}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Список закончился. Выберите действие', 'greeting')
        elif self.event.text == "выйти":
            self.write_msg_keyboard('Выбери действие', 'greeting')
            objects[0].state = "Initial"
            self.session.commit()
        else:
            if orm.show_ignore_user():
                self.write_msg_keyboard(f'https://vk.com/id{orm.show_ignore_user()}', 'show_users')
            else:
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Игнор список пуст. Выберите действие', 'greeting')

    def city_state(self, objects):
        if re.findall(r'[а-яА-яёЁ0-9]+', self.event.text):
            try:
                city_name = VkUser().get_city_name(VkUser().get_city_id(self.event.text))[0]['title']
                objects[0].state = "Sex"
                objects[1].search_city = city_name
                self.session.commit()
                self.write_msg('Введите пол:\n1 - женщина\n2 - мужчина')
            except IndexError:
                self.write_msg('Вы ввели неверную информацию\nВведите город повторно')
        else:
            self.write_msg('Вы ввели неверную информацию\nВведите город повторно')

    def sex_state(self, objects):
        if self.event.text == '1' or self.event.text == '2':
            try:
                sex_value = int(self.event.text)
                objects[0].state = "Relation"
                objects[1].search_sex = sex_value
                self.session.commit()
                self.write_msg('Введите семейное положение\n1 — не женат/не замужем\n2 — есть друг/есть подруга\n'
                               '3 — помолвлен/помолвлена\n4 — женат/замужем\n5 — всё сложно\n6 — в активном поиске\n'
                               '7 — влюблён/влюблена\n8 — в гражданском браке\n0 — не указано\n')
            except ValueError:
                self.write_msg('Вы ввели неверную информацию\nВведите пол повторно\n1 - женщина\n2 - мужчина')
        else:
            self.write_msg('Вы ввели неверную информацию\nВведите пол повторно\n1 - женщина\n2 - мужчина')

    def relation_state(self, objects):
        msg = f'Вы ввели неверную информацию\nВведите статус повторно\n1 — не женат/не замужем\n' \
              f'2 — есть друг/есть подруга\n3 — помолвлен/помолвлена\n4 — женат/замужем\n5 — всё сложно\n' \
              f'6 — в активном поиске\n7 — влюблён/влюблена\n8 — в гражданском браке\n0 — не указано\n'
        if re.findall(r'[0-8]', self.event.text):
            try:
                status = int(self.event.text)
                objects[0].state = "Range"
                objects[1].search_relation = status
                self.session.commit()
                self.write_msg('Введите диапозон поиска ОТ и ДО (через пробел или -)')
            except ValueError:
                self.write_msg(msg)
        else:
            self.write_msg(msg)

    def range_state(self, objects):
        try:
            age_pattern = re.compile(r'(\d\d?)[ -]+(\d\d?)')
            age_from = int(age_pattern.sub(r"\1", self.event.text))
            age_to = int(age_pattern.sub(r"\2", self.event.text))
            if age_to - age_from < 0:
                self.write_msg('Вы ввели неверную информацию\nВведите диапозон поиска ОТ и ДО (через пробел или -)')
                return
            objects[0].state = "Decision"
            objects[1].search_from = age_from
            objects[1].search_to = age_to
            self.session.commit()
            sex = objects[1].search_sex
            city_name = objects[1].search_city
            status = objects[1].search_relation
            vk_server.search_dating_user(age_from, age_to, sex, city_name, status)
            user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
            while user_founded_id is None:
                user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
            if len(attachment_list) >= 1:
                self.write_msg_attachment(f'{first_name} {last_name}', attachment_list, 'decision')
            else:
                self.write_msg(f'{first_name} {last_name} - {attachment_list}\n')
                self.write_msg_keyboard('Выберите действие', 'decision')
        except ValueError:
            self.write_msg('Вы ввели неверную информацию\nВведите диапозон поиска ОТ и ДО (через пробел или -)')

    def get_founded_user_info(self):
        person = vk_server.users_info_dict['items'][self.count]
        user_dating_id = person['id']
        # проверка наличия найденного Id в таблицах
        if orm.is_viewed(user_dating_id, self.event.user_id):
            self.count += 1
            return None, None, None, None, None
        first_name = person['first_name']
        last_name = person['last_name']
        attachment_list = []
        try:
            age = person['bdate']
            age = get_age(age)
        except KeyError:
            age = 'нет данных'
        link_dict = VkUser().get_users_best_photos(user_dating_id)
        try:
            for photo_links in link_dict.values():
                attachment = photo_links[0]
                attachment_list.append(f'photo{user_dating_id}_{attachment}')
            attachment_string = ",".join(attachment_list)
            return user_dating_id, first_name, last_name, age, attachment_string
        except AttributeError:
            link_dict = 'нет фотографий'
            return user_dating_id, first_name, last_name, age, link_dict

    def decision_state(self, objects):
        user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
        if self.event.text == "выход":
            objects[0].state = "Initial"
            self.session.commit()
            self.write_msg_keyboard('Выберите действие:', 'greeting')
            return
        elif self.event.text == "лайк":
            DatingUser().add_dating_user(user_founded_id, first_name, last_name, age, self.event.user_id)
            UserPhoto().add_user_photo(VkUser().get_users_best_photos(user_founded_id), user_founded_id,
                                       self.event.user_id)
            self.count += 1
            user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
            if len(attachment_list) >= 1:
                self.write_msg_attachment(f'{first_name} {last_name}', attachment_list, 'decision')
            else:
                self.write_msg(f'{first_name} {last_name} - {attachment_list}\n')
                self.write_msg_keyboard('Выберите действие', 'decision')
        elif self.event.text == "крестик":
            IgnoreUser().add_ignore_user(user_founded_id, self.event.user_id)
            self.count += 1
            user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
            if len(attachment_list) >= 1:
                self.write_msg_attachment(f'{first_name} {last_name}', attachment_list, 'decision')
            else:
                self.write_msg(f'{first_name} {last_name} - {attachment_list}\n')
                self.write_msg_keyboard('Выберите действие', 'decision')
        elif self.event.text == "пропуск":
            SkippedUser().add_skipped_user(user_founded_id, self.event.user_id)
            self.count += 1
            user_founded_id, first_name, last_name, age, attachment_list = self.get_founded_user_info()
            if len(attachment_list) >= 1:
                self.write_msg_attachment(f'{first_name} {last_name}', attachment_list, 'decision')
            else:
                self.write_msg(f'{first_name} {last_name} - {attachment_list}\n')
                self.write_msg_keyboard('Выберите действие', 'decision')
        else:
            return

    def use_state(self, state_name):
        self.states = {
            "Hello": self.hello_state,
            "Initial": self.initial_state,
            "City": self.city_state,
            "Sex": self.sex_state,
            "Relation": self.relation_state,
            "Range": self.range_state,
            "Decision": self.decision_state,
            "Like": self.like_state,
            "Ignore": self.ignore_state
        }
        return self.states[state_name]

    def start(self, ):
        for event in self.long_poll.listen():
            self.event = event
            if self.event.type != VkEventType.MESSAGE_NEW:
                continue
            if not self.event.to_me:
                continue
            if self.event.user_id != self.current_user_id:
                self.current_user_id = self.event.user_id
            else:
                pass
            if orm.looking_for_user_vk(self.event.user_id) is None:
                objects = orm.add_objects(self.event.user_id, VkUser().get_user_data(self.event.user_id))
            else:
                objects = orm.looking_for_user_vk(self.event.user_id)
            if self.event.text == '/start':
                objects[0].state = "Initial"
                self.session.commit()
                self.write_msg_keyboard('Выберите действие:', 'greeting')
            elif self.event.text == '/test':
                pass
            else:
                ans = self.use_state(objects[0].state)(objects)
                # при нажатии Выход ans возвращает False и выходим из программы
                if ans or ans is None:
                    continue
                else:
                    break
Beispiel #19
0
class VK:
    __slots__ = ('vk', 'logger', 'event_queue', 'msg_queue', 'user_cache',
                 'group_id')

    def __init__(self, token: str, logger) -> None:
        self.vk = VkApi(token=token, api_version=API_VERSION)
        self.logger = logger

        self.event_queue = collections.deque()
        self.msg_queue = []
        self.user_cache = {}

        self.group_id = self.method('groups.getById')[0]['id']

        self.init_group_settings()

    def method(self, method: str, args: dict = None) -> dict:
        return self.vk.method(method, args)

    def send(self,
             peer_id: int,
             message: str,
             keyboard=None,
             attach=None,
             sticker=None,
             disable_mentions=True) -> None:
        if 4000 < len(message) < 100000 and (not attach) and (not sticker):
            for message_part in [
                    message[j:j + 4000] for j in range(0, len(message), 4000)
            ]:
                self.msg_queue.append({
                    'peer_id': peer_id,
                    'message': message_part,
                    'random_id': get_random_id(),
                    'disable_mentions': disable_mentions,
                    'keyboard': keyboard
                })

        else:
            self.msg_queue.append({
                'peer_id': peer_id,
                'message': message,
                'random_id': get_random_id(),
                'disable_mentions': disable_mentions,
                'keyboard': keyboard,
                'attachment': attach,
                'sticker_id': sticker
            })

    def send_multiple(self,
                      peer_ids: List[int],
                      message: str,
                      keyboard=None,
                      disable_mentions=True) -> None:
        self.msg_queue.append({
            'peer_ids': peer_ids,
            'message': message,
            'random_id': get_random_id(),
            'disable_mentions': disable_mentions,
            'keyboard': keyboard
        })

    def get_user_link(self, target_id: int, name_case: str = 'nom') -> str:
        if target_id not in self.user_cache and target_id != 0:
            if target_id < 0:
                self.user_cache[target_id] = self.method(
                    'groups.getById', {'group_id': -target_id})[0]

            else:
                self.user_cache[target_id] = self.method(
                    'users.get', {
                        'user_ids': target_id,
                        'name_case': name_case
                    })[0]

        if target_id < 0:
            return ''.join([
                '[id',
                str(target_id), '|', self.user_cache[target_id]['first_name'],
                ']'
            ])

        elif target_id == 0:
            return '@id0'

        else:
            self.user_cache[target_id] = self.method('users.get', {
                'user_ids': target_id,
                'name_case': name_case
            })[0]
            return f"[id{target_id}|{self.user_cache[target_id]['first_name']}]"

    def get_user_links(self, target_ids: List[int]) -> dict:
        cached = True
        for i in target_ids:
            if i not in self.user_cache:
                cached = False
                break

        if not cached:
            for i in self.method(
                    'users.get',
                {'user_ids': ','.join(list(map(str, target_ids)))}):
                self.user_cache[i['id']] = i

        return {
            i: f"[id{i}|{self.user_cache[i]['first_name']}]"
            for i in target_ids
        }

    def get_target_id(self, s: str) -> Optional[int]:
        r = s.replace('https://', '').replace('vk.com/', '').replace(
            '@id', '').replace('@', '').replace('[', '').replace(']', '')

        if '|' in r:
            r = r.split('|')[0]

        if not r.isdecimal():
            r = self.method('utils.resolveScreenName',
                            {'screen_name': r.replace('-', 'club')})
            if not r:
                return

            if r['type'] == 'user':
                r = r['object_id']
            elif r['type'] == 'group':
                r = -r['object_id']

        return int(r)

    def is_chat_member(self, peer_id: int, user_id: int) -> bool:
        members = self.method('messages.getConversationMembers',
                              {'peer_id': peer_id})['items']
        for i in members:
            if i['member_id'] == user_id:
                return True

    def is_chat_admin(self,
                      peer_id: int,
                      user_id: int,
                      check_if_owner: bool = False) -> bool:
        members = self.method('messages.getConversationMembers',
                              {'peer_id': peer_id})['items']
        for i in members:
            if i['member_id'] == user_id and 'is_admin' in i and i[
                    'is_admin'] and ((not check_if_owner) or
                                     ('is_owner' in i and i['is_owner'])):
                return True

    def get_chat_owner(self, peer_id: int) -> Optional[int]:
        members = self.method('messages.getConversationMembers',
                              {'peer_id': peer_id})['items']
        for i in members:
            if 'is_owner' in i and i['is_owner']:
                return i['member_id']

    def get_upload(self) -> VkUpload:
        return VkUpload(self.vk)

    def init_group_settings(self) -> None:
        self.method(
            'groups.setSettings', {
                'group_id': self.group_id,
                'messages': 1,
                'bots_capabilities': 1,
                'bots_start_button': 1,
                'bots_add_to_chat': 1,
            })

        self.method(
            'groups.setLongPollSettings', {
                'group_id': self.group_id,
                'enabled': 1,
                'api_version': API_VERSION,
                'message_new': 1,
            })

    async def messages_sender(self) -> None:
        while True:
            queue = self.msg_queue[:25]

            if queue:
                self.msg_queue = self.msg_queue[25:]

                try:
                    vk_execute(
                        self.vk, ''.join(('API.messages.send(' + json.dumps(
                            i, ensure_ascii=False, separators=(',', ':')) +
                                          ');') for i in queue))
                except Exception as ex:
                    self.logger.warning(
                        'Произошла ошибка при отправке сообщений', exc_info=ex)

            await asyncio.sleep(0.05)

    @threaded
    def event_handler(self) -> None:
        convs = self.method('messages.getConversations', {
            'count': 200,
            'filter': 'unanswered'
        })['items']
        for i in convs:
            self.event_queue.append(VKMessage(i['last_message'], self))

        lp = VkBotLongPoll(self.vk, self.group_id)

        while True:
            try:
                for event in lp.check():
                    if event.type == VkBotEventType.MESSAGE_NEW:
                        self.event_queue.append(
                            VKMessage(event.raw['object']['message'], self))

                    else:
                        self.event_queue.append(event)

            except Exception as ex:
                self.logger.warning('Произошла ошибка в LongPoll', exc_info=ex)
                time.sleep(3)
Beispiel #20
0
class VkBot:
    """ Main class
    :param config: Configuration file path
    :type config: str
    
    :param logger: Logging config file path
    :type logger: str
    """
    MESSAGES = {}
    RUNNING = True
    admins = []
    prefix = None
    events = {}
    config = None
    logger = None
    lang = "rus"
    time = 0
    vk = None
    lp = None

    def __init__(self, config, logger):
        self.__load_configs(config, logger)
        self.logger = logging.getLogger(self.get_settings("logger", "vkbot"))
        self.vk = VkApi(token=self.get_settings("access_token"),
                        api_version=self.get_settings("version", 5.120))
        self.admins = self.get_settings("admins", "").split(",")
        self.prefix = self.get_settings("prefix", None)
        self.lp = VkBotLongPoll(self.vk, self.get_settings("group_id"),
                                int(self.get_settings("lp_wait", 5)))
        self.register(VkBotEventType.MESSAGE_NEW, self.cmd_event)
        self.cmd = CommandManager(self)
        self.tasks = TaskManager(self)
        th = threading.Thread(target=self.__exit)
        th.daemon = True
        th.start()

        self.logger.info(
            self.get_messages("start", id=self.get_settings("group_id")))
        self.time = utime()

    def task(self, time, repeating=0):
        """ Add task with decorator
        
        :param time: complete time
        :param repeating: repeating count
        """
        def tsk(func):
            tsk = Task(time, repeating)
            tsk.target = func
            tsk.name = func.__name__
            self.tasks.add_task(tsk)
            return func

        return tsk

    def command(self, name, **kwargs):
        """ Register command with decorator
        
        :param kwargs: Command constructor args
        """
        def cmd(func):
            cmd = Command(name=name, **kwargs)
            cmd.target = func
            self.cmd.register(cmd)
            return func

        return cmd

    def event(self, event):
        """ Register event with decorator
        
        :param event: Event type
        :type event: str
        """
        def register(func):
            self.register(event, func)
            return func

        return register

    def cmd_event(self, obj):
        user = self.get_object(obj.from_id)
        chat = self.get_chat(obj.peer_id)
        if self.prefix:
            if obj.text[:1] == self.prefix[:1]:
                self.cmd.exec(user, chat, obj, obj.text[1:])
        else:
            self.cmd.exec(user, chat, obj, obj.text)

    def get_id(self, id):
        """
        :param id: Id|domain|link
        :returns: available id
        """
        if not isinstance(id, str):
            return id

        try:
            for r in ("@", "*", "[", "]", "id", "club", "https://vk.com/"):
                id = id.replace(r, "")
            id = id.split("|")
            id = id[0]
            if not id.isnumeric():
                type = self.method("messages.getConversationsById",
                                   peer_ids=id)["items"][0]["peer"]["type"]
                if type == "group":
                    id = -self.method("groups.getById", group_ids=id)[0]["id"]
                elif type == "user":
                    id = self.method("users.get", user_ids=id)[0]["id"]
        except:
            pass
        return id

    def get_object(self, id):
        """
        :param id: id|domain|link
        :returns: User|Sender|Chat|Group
        """
        id = self.get_id(id)
        type = self.method("messages.getConversationsById", peer_ids=id)
        type = type["items"][0]["peer"]["type"]
        if type == "chat":
            return self.get_chat(id)
        elif type == "user":
            return self.get_user(id)
        elif type == "group":
            return self.get_group(id)
        return Sender(self, id)

    def get_group(self, id):
        return Group(self, self.get_id(id))

    def get_chat(self, id):
        return Chat(self, id)

    def get_user(self, id):
        return User(self, self.get_id(id))

    def method(self, method, **kwargs):
        """ Call the api method
        
        :param method: str
        :param kwargs: method params
        """
        try:
            response = self.vk.method(method, kwargs)
        except ApiError as e:
            self.logger.error(
                self.get_messages("method_error", method=method, message=e))
            seld.log_exception()
            response = []
        return response

    def register(self, event, consumer):
        """ Register event
        
        :param event: str
        :param consumer: func
        """
        if event not in self.events:
            self.events[event] = []
        self.events[event].append(consumer)

    def __handle(self, event, obj):
        if event not in self.events:
            return

        for e in self.events[event]:
            if callable(e):
                try:
                    e(obj)
                except:
                    self.logger.error(
                        self.get_messages("event_error", event=event))
                    self.log_exception()

    def start(self):
        while self.RUNNING:
            try:
                for event in self.lp.check():
                    thread = threading.Thread(target=self.__handle,
                                              args=(event.type, event.obj))
                    thread.daemon = True
                    thread.start()
            except:
                self.log_exception()
        else:
            self.logger.info(self.get_messages("stop_listen"))
            self.logger.info(self.get_messages("stop", ut=self.uptime()))

    def log_exception(self):
        self.logger.debug(format_exc())

    def get_messages(self, key, **args):
        """
        :param key: Messages key
        :type key: str
        
        :param args: kwargs
        :returns: str
        """
        if not self.config:
            return key
        else:
            try:
                msg = self.config.get("messages", key)
            except:
                msg = self.MESSAGES[self.lang].get(key)
            if msg:
                return msg.format(**args)
            else:
                return key

    def get_settings(self, key, default=None):
        """
        :param key: Settings key
        :type key: str
        
        :param default: if the key is not found in the config, it will return the default value
        :type default: mixed
        
        :returns: mixed
        """
        if not self.config:
            return default
        else:
            try:
                res = self.config.get("settings", key)
                return res
            except:
                return default

    def uptime(self):
        time = utime() - self.time
        fmt = self.get_messages("time_format").split(",")

        months = round(time / 2592000 % 999)
        weeks = round(time / 604800 % 4)
        days = round(time / 86400 % 7)
        hours = round(time / 3600 % 24)
        minutes = round(round(time / 60) % 60)

        if months > 0:
            return "{}{} {}{}".format(months, fmt[0], weeks, fmt[1])

        format = ""

        if days > 0:
            format += "{}{} ".format(days, fmt[2])
        if hours > 0:
            format += "{}{} ".format(hours, fmt[3])
        if minutes > 0:
            format += "{}{} ".format(minutes, fmt[4])
        format += "{}{}".format(round(time % 60), fmt[5])
        return format

    def __exit(self):
        input()
        self.stop()

    def stop(self):
        self.RUNNING = False

    def __load_configs(self, config, logger):
        logging.config.fileConfig(logger)
        self.config = ConfigParser()
        self.config.sections()
        self.config.read(config)

        self.MESSAGES["rus"] = {
            "start": "Бот запущен. ID:{id}",
            "stop": "Бот остановлен. Время работы: {ut}",
            "time_format": "мес.,нед.,дн.,чс.,мн.,сек.",
            "stop_listen": "Остановка прослушивания..",
            "event_error": "Не удалось обработать событие '{event}'",
            "method_error":
            "ApiError: не удалось вызвать метод '{method}' - {message}",
            "task_error": "Не удалось выполнить задачу '{task}'",
            "no_permission": "У вас недостаточно прав!",
            "command_not_found": "Комада {cmd} не найдена",
            "command_error": "При выполнении команды {cmd} произошла ошибка.",
            "group_not_use":
            "Эту команду могут использовать только пользователи!"
        }
        self.MESSAGES["eng"] = {
            "start": "Bot started. ID:{id}",
            "stop": "Bot stopped. Uptime: {ut}",
            "time_format": "mh.,w.,d.,h.,m.,s.",
            "stop_listen": "Stop listening..",
            "event_error": "Failed to execute method '{event}'",
            "method_error":
            "ApiError: Failed to call method '{method}' - {message}",
            "task_error": "failed to complete the task '{task}'",
            "no_permission": "You do not have sufficient permissions!",
            "command_not_found": "Command {cmd} not found",
            "command_error":
            "An error occurred while executing the {cmd} command.",
            "group_not_use": "This command can only be used by users!"
        }

        if not self.get_settings("access_token"):
            raise Exception("specify the access token in the config file!")
        if not self.get_settings("group_id"):
            raise Exception("specify the group id in the config file!")
Beispiel #21
0
def main():
    init()

    global config
    try:
        with open('config.json') as f:
            config = json.load(f)

    except FileNotFoundError:
        config = {
            'limit': 1000,
            'types': {
                'chats': True,
                'groups': True,
                'users': True
            }
        }

        with open('config.json', 'w') as f:
            json.dump(config, f, indent=2)

    if config['limit'] <= 0:
        config['limit'] = 1e10

    s = input('Введите "логин:пароль" или токен: ')

    if len(s) == 85:
        vk = VkApi(token=s, captcha_handler=captcha_handler)

        vk.http.headers.update({'User-agent': USER_AGENT})

    elif ':' in s:
        sp = s.split(':')
        vk = VkApi(sp[0],
                   sp[1],
                   app_id=2685278,
                   captcha_handler=captcha_handler)

        vk.http.headers.update({'User-agent': USER_AGENT})

        try:
            vk.auth(token_only=True)
        except AuthError:
            con(Colors.ERROR + 'Неверный логин или пароль')
            return
    else:
        con(Colors.WARNING +
            'Введите данные для входа в виде "логин:пароль" или "токен"')
        return

    try:
        user = vk.method('users.get')[0]
    except VkApiError as ex:
        if ex.__dict__['error']['error_code'] == 5:
            error_text = 'неверный токен'
        else:
            error_text = str(ex)

        con(Colors.ERROR + 'Ошибка входа: ' + error_text)
        return

    con(Colors.OK + 'Вход выполнен')

    infos = []

    count = vk.method('messages.getConversations', {'count': 0})['count']

    for offset in range((count - 1) // 200 + 1):
        peers = vk.method('messages.getConversations', {
            'count': 200,
            'extended': 1,
            'offset': offset * 200
        })

        for peer in peers['items']:
            peer_id = peer['conversation']['peer']['id']
            peer_type = peer['conversation']['peer']['type']

            if peer_type == 'group' and config['types']['groups']:
                info = [i for i in peers['groups'] if i['id'] == -peer_id][0]
                name = info[
                    'screen_name'] if 'screen_name' in info else 'club' + str(
                        info['id'])
                infos.append((peer_id, name, info['name']))

            elif peer_type == 'user' and config['types']['users']:
                info = [i for i in peers['profiles'] if i['id'] == peer_id][0]
                name = info[
                    'screen_name'] if 'screen_name' in info else 'id' + str(
                        info['id'])
                infos.append((peer_id, name,
                              info['first_name'] + ' ' + info['last_name']))

            elif peer_type == 'chat' and config['types']['chats']:
                infos.append((peer_id, '',
                              peer['conversation']['chat_settings']['title']))

    base_dir = 'messages_' + str(user['id']) + '/'
    if not os.path.exists(base_dir):
        os.makedirs(base_dir)

    copy('files/favicon.ico', base_dir)
    copy('files/style.css', base_dir)

    name = user['first_name'] + ' ' + user['last_name']

    with open('files/index_pre.html', encoding='utf-8') as f:
        index_file = f.read().replace('\n', '').format(user['id'], name)

    for i in infos:
        if i[1]:
            index_file += '<div class="item"><div class="item__main"><a href="messages/{0}.html">{1}</a></div><div class="item__tertiary">@{2}</div></div>'.format(
                i[0], i[2], i[1])
        else:
            index_file += '<div class="item"><a href="messages/{0}.html">{1}</a></div>'.format(
                i[0], i[2])

    with open('files/index_post.html', encoding='utf-8') as f:
        index_file += f.read().replace('\n', '')

    with open(base_dir + 'index.html', 'w', encoding='utf-8') as f:
        f.write(index_file)

    messages_dir = base_dir + 'messages/'
    if not os.path.exists(messages_dir):
        os.makedirs(messages_dir)

    total = ' \\ ' + str(len(infos))

    for num in range(len(infos)):
        info = infos[num]
        msgs = []
        msg_count = vk.method('messages.getHistory', {
            'peer_id': info[0],
            'count': 0
        })['count']

        con(Colors.INFO + 'Сохранение диалога ' + str(num + 1) + total)

        for offset in range(
                min(((msg_count - 1) // 200 + 1), config['limit'] // 200)):
            try:
                chunk = vk.method(
                    'messages.getHistory', {
                        'peer_id': info[0],
                        'count': 200,
                        'extended': 1,
                        'offset': offset * 200
                    })

                for msg in chunk['items']:
                    if msg['from_id'] < 0:
                        item = [
                            i for i in chunk['groups']
                            if i['id'] == -msg['from_id']
                        ][0]
                        sender = 'club' + str(-msg['from_id'])
                        sender_name = item['name']

                    else:
                        item = [
                            i for i in chunk['profiles']
                            if i['id'] == msg['from_id']
                        ][0]
                        sender = 'id' + str(msg['from_id'])
                        sender_name = item['first_name'] + ' ' + item[
                            'last_name']

                    text = 'Служебное сообщение ' + msg['action'][
                        'type'] if 'action' in msg else msg['text']

                    a = []
                    for i in msg['attachments']:
                        link = ''

                        if i['type'] == 'photo':
                            photo = ''
                            current = 0
                            sizes = i['photo']['sizes']
                            for size in sizes:
                                if size['type'] == 'w':
                                    photo = size['url']
                                    break
                                elif size['type'] == 's' and current < 1:
                                    current = 1
                                    photo = size['url']
                                elif size['type'] == 'm' and current < 2:
                                    current = 2
                                    photo = size['url']
                                elif size['type'] == 'x' and current < 3:
                                    current = 3
                                    photo = size['url']
                                elif size['type'] == 'y' and current < 4:
                                    current = 4
                                    photo = size['url']
                                elif size['type'] == 'z' and current < 5:
                                    current = 5
                                    photo = size['url']

                            desc = 'Фотография'
                            link = photo

                        elif i['type'] == 'video':
                            desc = 'Видеозапись'
                            link = 'https://vk.com/video' + str(
                                i['video']['owner_id']) + '_' + str(
                                    i['video']['id'])

                        elif i['type'] == 'audio':
                            desc = 'Аудиозапись ' + i['audio']['title']

                        elif i['type'] == 'doc':
                            desc = 'Документ'
                            link = i['doc']['url']

                        elif i['type'] == 'link':
                            desc = 'Ссылка'
                            link = i['link']['url']

                        elif i['type'] == 'market':
                            desc = 'Товар'
                            link = 'https://vk.com/market' + str(
                                i['market']['owner_id']) + '_' + str(
                                    i['market']['id'])

                        elif i['type'] == 'wall':
                            desc = 'Запись на стене'
                            link = 'https://vk.com/wall' + str(
                                i['wall']['to_id']) + '_' + str(
                                    i['wall']['id'])

                        elif i['type'] == 'wall_reply':
                            if 'deleted' in i['wall_reply']:
                                desc = 'Комментарий на стене (удалён)'
                            else:
                                desc = 'Комментарий на стене'
                                link = 'https://vk.com/wall' + str(
                                    i['wall_reply']['owner_id']) + '_' + str(
                                        i['wall_reply']['post_id']
                                    ) + '?reply=' + str(i['wall_reply']['id'])

                        elif i['type'] == 'sticker':
                            desc = 'Стикер ID ' + str(
                                i['sticker']['sticker_id'])

                        elif i['type'] == 'gift':
                            desc = 'Подарок ID ' + str(i['gift']['id'])

                        elif i['type'] == 'audio_message':
                            desc = 'Аудиосообщение'
                            link = i['audio_message']['link_mp3']

                        elif i['type'] == 'poll':
                            desc = 'Опрос'
                            link = 'https://vk.com/poll' + str(
                                i['poll']['owner_id']) + '_' + str(
                                    i['poll']['id'])

                        else:
                            desc = ''

                        attach = '<div class="attachment__description">' + desc + '</div>'
                        if link:
                            attach += '<a class="attachment__link" href="' + link + '" target="_blank">' + link + '</a>'

                        a.append(attach)

                    msgs.append((sender, msg['date'], sender_name, text, a))

            except KeyError:
                con(Colors.WARNING + 'Ошибка при сохранении диалога ' +
                    str(info[0]))

        with open('files/messages_pre.html', encoding='utf-8') as f:
            file = f.read().replace('\n', '').format(user['id'], name, info[2])

        for msg in msgs:
            link = '<a href="https://vk.com/' + str(
                msg[0]) + '" target="_blank">' + msg[2] + '</a>, '
            tm = time.strftime('%d.%m.%Y %H:%M:%S', time.gmtime(msg[1] + TZ))
            attach = '<div class="kludges">' + '\n'.join(
                msg[4]) + '</div>' if msg[4] else ''

            file += '<div class="item"><div class="item__main"><div class="message"><div class="message__header">{0}{2}</div><div>{1}</div></div></div></div>'.format(
                link + tm, msg[3], attach)

        with open('files/index_post.html', encoding='utf-8') as f:
            file += f.read().replace('\n', '')

        with open(messages_dir + str(info[0]) + '.html', 'w',
                  encoding='utf-8') as f:
            f.write(file)

    con(Colors.OK + 'Готово!')
Beispiel #22
0
class SearchHandler(BaseHandler):

    def __init__(self, user_id, vk_group_client, db_session):
        self.questions = []
        self.search_params = {}
        self.vk_user_client = None
        self.db_session = db_session
        super().__init__(user_id, vk_group_client)

    def reset(self):
        self.search_params = {}
        self.questions = [
            AgeFromQuestion(self.user_id, self.vk_group_client),
            AgeToQuestion(self.user_id, self.vk_group_client),
            HometownQuestion(self.user_id, self.vk_group_client),
            SexQuestion(self.user_id, self.vk_group_client),
            StatusQuestion(self.user_id, self.vk_group_client),
            TokenQuestion(self.user_id, self.vk_group_client, self.db_session),
        ]

    def keywords(self):
        return ['Старт', 'Start']

    def is_active(self):
        for question in self.questions:
            if question.should_handle_answer():
                return True
        return False

    def handle_impl(self, message):
        if self.should_handle(message):
            self.reset()

        question = self.questions[0]
        if question.should_handle_answer():
            if not question.handle_answer(message, self.search_params):
                question.ask()
            else:
                self.questions.pop(0)

        while len(self.questions) > 0 and not self.is_active():
            question = self.questions[0]
            if question.should_ask():
                question.ask()
            else:
                self.questions.pop(0)

        if len(self.questions) == 0:
            self.find_match()

    def find_match(self):
        user = self.db_session.query(User).filter(User.id == self.user_id).first()
        self.vk_user_client = VkApi(token=user.token)
        candidates = self.search()

        if len(candidates) == 0:
            self.write_msg('Никого не найдено')
            return

        for candidate in candidates:
            top_photos = self.get_popular_profile_photos(candidate['id'])
            candidate['top_photos'] = top_photos
            result = f"{candidate['first_name']} {candidate['last_name']}\n"
            result += f"https://vk.com/{candidate['screen_name']}\n"

            candidate_exists = self.db_session.query(Candidate).filter(Candidate.id == candidate['id']).first()
            if not candidate_exists:
                c = Candidate(
                    id=candidate['id'],
                    first_name=candidate['first_name'],
                    last_name=candidate['last_name'],
                    screen_name=candidate['screen_name'],
                )
                self.db_session.add(c)
                user.candidates.append(c)
            else:
                user.candidates.append(candidate_exists)

            self.write_msg(result)

            attachments = []
            for photo in top_photos:
                owner_photo_id = f"photo{photo['owner_id']}_{photo['id']}"
                attachments.append(owner_photo_id)
                photo_exists = self.db_session.query(Photo).filter(Photo.id == owner_photo_id).first()
                if not photo_exists:
                    self.db_session.add(
                        Photo(
                            id=owner_photo_id,
                            photo_id=photo['id'],
                            candidate_id=photo['owner_id'],
                            likes_count=photo['likes']['count'],
                            comments_count=photo['comments']['count'],
                        )
                    )
            self.send_attachment(','.join(attachments))
        self.db_session.commit()

    def send_attachment(self, attachment):
        self.vk_group_client.method('messages.send', {'user_id': self.user_id, 'attachment': attachment,
                                                      'random_id': randrange(10 ** 7)})

    def get_popular_profile_photos(self, owner_id):
        response = self.vk_user_client.method('photos.get', {'owner_id': owner_id, 'album_id': 'profile', 'extended': 1,
                                                             'count': 1000})
        items = response['items']

        def sum_likes_and_comments_count(item):
            return item['likes']['count'] + item['comments']['count']

        items.sort(key=sum_likes_and_comments_count)

        most_liked_photos = items[-3:]
        return most_liked_photos

    def search(self):
        params = {'has_photo': 1, 'count': 10, 'fields': 'screen_name'}
        response = self.vk_user_client.method('users.search', {**self.search_params, **params})

        def already_matched(user_id, candidate_id):
            user = self.db_session.query(User).filter(User.id == user_id, User.candidates.any(id=candidate_id)).first()
            return user is not None

        items = [item for item in response['items'] if
                 not item['is_closed'] and not already_matched(self.user_id, item['id'])]
        return items