def _start_amazon(): url = env.str("MINECRAFT_1_12_2_START_URL") url_status = env.str("MINECRAFT_1_12_2_STATUS_URL") response = requests.get(url_status).json() if response['InstanceState']['Name'] == 'stopped': requests.post(url) else: raise PWarning( f"Сервер сейчас имеет состояние {response['InstanceState']['Name']}, не могу запустить")
def set_device_state(device, state): if 'topic' not in device: raise PWarning("Этим устройством невозможно управлять") client = mqtt.Client("petrovich") client.username_pw_set(username=env.str('MQTT_USER'), password=env.str('MQTT_PASSWORD')) client.connect(env.str('MQTT_SERVER')) client.publish(device['topic'], state) client.disconnect()
def handle(self, *args, **kwargs): url = "https://taxi-routeinfo.taxi.yandex.net/taxi_info" params = { 'clid': env.str('YANDEX_TAXI_CLID'), 'apikey': env.str('YANDEX_TAXI_API_KEY'), 'rll': env.str('YANDEX_TAXI_TEST_COORDS'), 'class': 'econom,business,comfortplus,express,courier', } response = requests.get(url, params).json() TaxiInfo(data=response).save()
def __init__(self): CommonBot.__init__(self, Platform.VK) self.token = env.str('VK_BOT_TOKEN') self.group_id = env.str('VK_BOT_GROUP_ID') vk_session = VkApi(token=self.token, api_version="5.131", config_filename="secrets/vk_bot_config.json") self.longpoll = MyVkBotLongPoll(vk_session, group_id=self.group_id) self.upload = VkUpload(vk_session) self.vk = vk_session.get_api()
def send_rcon(command): try: with MCRcon(env.str("MINECRAFT_1_12_2_IP"), env.str("MINECRAFT_1_12_2_RCON_PASSWORD")) as mcr: resp = mcr.command(command) if resp: return resp else: return False except Exception: return False
def __init__(self): super().__init__() self.id = env.int('VK_USER_ID') vk_session = vk_api.VkApi( env.str('VK_USER_LOGIN'), env.str('VK_USER_PASSWORD'), auth_handler=self.auth_handler, config_filename=f"{BASE_DIR}/secrets/vk_user_config.json") vk_session.auth() self.upload = VkUpload(vk_session) self.vk = vk_session.get_api()
def get_images_urls(self, query): querystring = { "key": env.str("GOOGLE_API_KEY"), "cx": env.str("GOOGLE_SEARCH_ENGINE_ID"), "searchType": "image", "safe": "active", "q": query } response = requests.get(self.URL, params=querystring).json() images_urls = [] if 'items' in response: images_urls = [x['link'] for x in response['items']] return images_urls
def __init__(self): Thread.__init__(self) CommonBot.__init__(self, Platform.VK) self.token = env.str('VK_BOT_TOKEN') self.group_id = env.str('VK_BOT_GROUP_ID') vk_session = VkApi(token=self.token, api_version="5.107", config_filename="secrets/vk_bot_config.json") self.longpoll = MyVkBotLongPoll(vk_session, group_id=self.group_id) self.upload = VkUpload(vk_session) self.vk = vk_session.get_api() self.vk_user = VkUser() self.test_chat = Chat.objects.get(chat_id=env.str("VK_TEST_CHAT_ID"))
def handle(self, *args, **options): url = "https://www.donationalerts.com/api/v1/alerts/donations" headers = { 'Authorization': 'Bearer ' + env.str('DONATIONALERT_ACCESS_TOKEN') } response = requests.get(url, headers=headers).json() donations, created = Service.objects.get_or_create(name='donations') if created: donations.value = response['meta']['total'] donations.save() return new_donation_count = int(response['meta']['total']) - int( donations.value) donations.value = response['meta']['total'] donations.save() if new_donation_count > 0: if new_donation_count == 1: result_str = 'Новый донат!\n\n' else: result_str = 'Новые донаты!\n\n' for i in range(new_donation_count): donation = response['data'][i] new_donation = Donations(username=donation['username'], amount=donation['amount'], currency=donation['currency'], message=donation['message']) new_donation.save() result_str += f"{donation['username']} - {donation['amount']} {donation['currency']}:\n" \ f"{donation['message']}\n\n" chat_pks = options['chat_id'][0].split(',') for chat_pk in chat_pks: chat = Chat.objects.get(pk=chat_pk) tg_bot.parse_and_send_msgs(result_str, chat.chat_id)
def __init__(self): Thread.__init__(self) CommonBot.__init__(self, Platform.TG) self.token = env.str("TG_TOKEN") self.requests = TgRequests(self.token) self.longpoll = MyTgBotLongPoll(self.token, self.requests)
class YandexGeoAPI: url = "https://geocode-maps.yandex.ru/1.x/" API_KEY = env.str("YANDEX_GEO_TOKEN") def get_address(self, lat, lon): params = { 'apikey': self.API_KEY, 'geocode': f"{lat, lon}", 'format': 'json', 'result': '1', 'lang': 'ru_RU' } result = requests.get(self.url, params).json() return result['response']['GeoObjectCollection']['featureMember'][0][ 'GeoObject']['metaDataProperty']['GeocoderMetaData']['text'] def get_city_info_by_name(self, city_name): params = { 'apikey': self.API_KEY, 'geocode': city_name, 'format': 'json', 'result': '1', 'lang': 'ru_RU' } result = requests.get(self.url, params).json() result2 = result['response']['GeoObjectCollection']['featureMember'] if len(result2) == 0: return None city_data = result2[0]['GeoObject'] lon, lat = city_data['Point']['pos'].split(' ', 2) city_name = city_data['name'] city_info = {'lat': lat, 'lon': lon, 'name': city_name} return city_info
def send_weather_request(self): token = env.str("YANDEX_WEATHER_TOKEN") params = {'lat': self.city.lat, 'lon': self.city.lon, 'lang': 'ru_RU'} headers = {'X-Yandex-API-Key': token} response = requests.get(self.url, params, headers=headers).json() if 'status' in response: if response['status'] == 403: raise PWarning( "На сегодня я исчерпал все запросы к Yandex Weather :(") fact = response['fact'] weather = { 'now': { 'temp': fact['temp'], 'temp_feels_like': fact['feels_like'], 'condition': fact['condition'], 'wind_speed': fact['wind_speed'], 'wind_gust': fact['wind_gust'], 'pressure': fact['pressure_mm'], 'humidity': fact['humidity'], }, 'forecast': [] } # Проставление part_name для времени сейчас index = list(DAY_TRANSLATOR.keys()).index( response['forecast']['parts'][0]['part_name']) weather['now']['part_name'] = list(DAY_TRANSLATOR.keys())[index - 1] for x in response['forecast']['parts']: weather['forecast'].append({ 'part_name': x['part_name'], 'temp_min': x['temp_min'], 'temp_max': x['temp_max'], 'temp_feels_like': x['feels_like'], 'condition': x['condition'], 'wind_speed': x['wind_speed'], 'wind_gust': x['wind_gust'], 'pressure': x['pressure_mm'], 'humidity': x['humidity'], 'prec_mm': x['prec_mm'], 'prec_period': int(int(x['prec_period']) / 60), 'prec_prob': x['prec_prob'], }) return weather
class SpotifyAPI: CLIENT_ID = env.str("SPOTIFY_CLIENT_ID") CLIENT_SECRET = env.str("SPOTIFY_CLIENT_SECRET") def __init__(self): self.sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials( client_id=self.CLIENT_ID, client_secret=self.CLIENT_SECRET)) def search_music(self, q, limit): results = self.sp.search(q=q, limit=limit, market="RU") return [{ 'artists': [y['name'] for y in x['artists']], 'album': { 'title': x['album']['name'], 'image': x['album']['images'][1]['url'] }, 'name': x['name'], 'url': x['external_urls']['spotify'] } for x in results['tracks']['items']]
class TimezoneDBAPI: URL = "http://api.timezonedb.com/v2.1/get-time-zone" API_KEY = env.str("TIMEZONEDB_API_KEY") def get_timezone_by_coordinates(self, lat, lon): params = { 'key': self.API_KEY, 'format': 'json', 'by': 'position', 'lat': lat, 'lng': lon } result = requests.get(self.URL, params=params).json() return result['zoneName']
def _stop_amazon(self): if not self.check_amazon_server_status(): return False self.send_rcon('/stop') while True: server_is_offline = not self.send_rcon('/help') if server_is_offline: break time.sleep(5) url = env.str("MINECRAFT_1_12_2_STOP_URL") requests.post(url) Service.objects.filter(name=f'stop_{self._get_service_name()}').delete() return True
def check_sender(self, role: Role): """ Проверка на роль отправителя :param role: требуемая роль :return: bool """ if self.event.sender.check_role(role): if role == Role.ADMIN: if self.event.platform == Platform.VK: if self.event.sender.user_id == env.str("VK_ADMIN_ID"): return True elif self.event.platform == Platform.TG: if self.event.sender.user_id == env.str("TG_ADMIN_ID"): return True else: print("Попытка доступа под админом не с моего id O_o") raise PError("Э ты чё, ты не админ. Где мой админ???") else: return True if role == Role.CONFERENCE_ADMIN: if self.event.chat.admin == self.event.sender: return True error = f"Команда доступна только для пользователей с уровнем прав {role.value}" raise PWarning(error)
def listen(self): for event in self.longpoll.listen(): try: # Если пришло новое сообщение if event.type == VkBotEventType.MESSAGE_NEW: vk_event = self._setup_event(event) if self.DEVELOP_DEBUG: from_test_chat = vk_event['chat_id'] == self.test_chat.id from_me = str(vk_event['user_id']) == env.str('VK_ADMIN_ID') if not from_test_chat or not from_me: continue # Сообщение либо мне в лс, либо упоминание меня, либо есть аудиосообщение, либо есть экшн if not self.need_a_response(vk_event): continue # Обработка вложенных сообщений в event['fwd']. reply и fwd для вк это разные вещи. if event.message.reply_message: vk_event['fwd'] = [event.message.reply_message] elif len(event.message.fwd_messages) != 0: vk_event['fwd'] = event.message.fwd_messages # Узнаём пользователя if vk_event['user_id'] > 0: vk_event['sender'] = self.get_user_by_id(vk_event['user_id']) else: self.send_message(vk_event['peer_id'], "Боты не могут общаться с Петровичем :(") continue # Узнаём конфу if vk_event['chat_id']: vk_event['chat'] = self.get_chat_by_id(int(vk_event['peer_id'])) if vk_event['sender'] and vk_event['chat']: self.add_chat_to_user(vk_event['sender'], vk_event['chat']) self.add_extra_group_to_user(vk_event['sender'], vk_event['chat']) else: vk_event['chat'] = None vk_event_object = VkEvent(vk_event) thread = threading.Thread(target=self.menu, args=(vk_event_object,)) thread.start() except Exception as e: print(str(e)) tb = traceback.format_exc() print(tb)
class GithubAPI: REPO_OWNER = 'Xoma163' REPO_NAME = 'petrovich' ISSUES_URL = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/issues' LABELS_URL = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/labels' TOKEN = env.str('GITHUB_TOKEN') HEADERS = {"Authorization": f"token {TOKEN}"} def create_issue(self, title, body=None, assignee=None, milestone=None, labels=None): """Создание issue.""" if labels is None: labels = [] issue_data = { 'title': title, 'body': body, 'assignee': assignee, 'milestone': milestone, 'labels': labels } r = requests.post(self.ISSUES_URL, json.dumps(issue_data), headers=self.HEADERS) if r.status_code != 201: raise PError("Не удалось создать issue на github") return r.json() def delete_issue(self, _id): issue_data = {"state": "closed", "labels": ["Не пофикшу"]} r = requests.post(f"{self.ISSUES_URL}/{_id}", json.dumps(issue_data), headers=self.HEADERS) if r.status_code != 200: raise PError("Не удалось закрыть issue на github") return r.json() def get_all_labels(self): r = requests.get(self.LABELS_URL, json.dumps({}), headers=self.HEADERS) return [x['name'] for x in r.json()]
class OCRApi: URL = 'https://api.ocr.space/parse/image' API_KEY = env.str("OCR_API_KEY") # def get_recognize_by_url(self, url, language): # payload = { # 'url': url, # 'apikey': self.API_KEY, # 'language': language, # } # return requests.post( # self.URL, # data=payload, # ).json() def get_recognize_by_bytes(self, file, language): payload = { 'filetype': 'JPG', 'apikey': self.API_KEY, 'language': language, } return requests.post( self.URL, files={ 'filename.jpg': file }, data=payload, ).json() def recognize(self, url_or_bytes, lang): response = self.get_recognize_by_bytes(url_or_bytes, lang) if 'OCRExitCode' in response: if response['OCRExitCode'] == 99: raise PError("Ошибка") if 'ParsedResults' not in response: raise PWarning("Ничего не распознал") text_list = [ x['ParsedText'].strip() for x in response['ParsedResults'] ] texts = "\n".join(text_list) return texts
def upload_image_to_tg_server(self, url) -> PhotoAttachment: """ Загрузка изображения на сервера ТГ с костылями Без бутылки не разобраться. У телеги нет встроенных методов по тупой загрузке файлов, поэтому приходится Отправлять сообщение в пустую конфу, забирать оттуда file_id и уже потом формировать сообщение """ photo_uploading_chat = self.chat_model.get(pk=env.str("TG_PHOTO_UPLOADING_CHAT_PK")) pa = PhotoAttachment() pa.public_download_url = url rm = ResponseMessage({'attachments': [pa]}, peer_id=photo_uploading_chat.chat_id) response = self.send_response_message_item(rm.messages[0]) if response.status_code != 200: raise PWarning r_json = response.json() self.delete_message(photo_uploading_chat.chat_id, r_json['result']['message_id']) uploaded_image = r_json['result']['photo'] if response.status_code == 200 else response pa.file_id = uploaded_image[-1]['file_id'] return pa
def get_moderator_chat_peer_id(): test_chat_id = env.str("TG_MODERATOR_CHAT_PK") return Chat.objects.get(pk=test_chat_id).chat_id
def __init__(self): self.image_quality_URL = 'https://api.everypixel.com/v1/quality_ugc' self.image_faces_URL = 'https://api.everypixel.com/v1/faces' self.CLIENT_ID = env.str("EVERYPIXEL_CLIENT_ID") self.CLIENT_SECRET = env.str("EVERYPIXEL_CLIENT_SECRET")
def check_amazon_server_status(): url = env.str("MINECRAFT_1_12_2_STATUS_URL") response = requests.get(url).json() return response['InstanceState']['Name'] == 'running'
def __init__(self): self.url = 'https://api.ocr.space/parse/image' self.api_key = env.str("OCR_API_KEY")
class YandexWeatherAPI: URL = "https://api.weather.yandex.ru/v1/informers" TOKEN = env.str("YANDEX_WEATHER_TOKEN") HEADERS = {'X-Yandex-API-Key': TOKEN} def __init__(self, city): self.city = city def send_weather_request(self): params = {'lat': self.city.lat, 'lon': self.city.lon, 'lang': 'ru_RU'} response = requests.get(self.URL, params, headers=self.HEADERS).json() if 'status' in response: if response['status'] == 403: raise PWarning( "На сегодня я исчерпал все запросы к Yandex Weather :(") fact = response['fact'] weather = { 'now': { 'temp': fact['temp'], 'temp_feels_like': fact['feels_like'], 'condition': fact['condition'], 'wind_dir': fact['wind_dir'], 'wind_speed': fact['wind_speed'], 'wind_gust': fact['wind_gust'], 'pressure': fact['pressure_mm'], 'humidity': fact['humidity'], }, 'forecast': [] } # Проставление part_name для времени сейчас index = list(DAY_TRANSLATOR.keys()).index( response['forecast']['parts'][0]['part_name']) weather['now']['part_name'] = list(DAY_TRANSLATOR.keys())[index - 1] for x in response['forecast']['parts']: weather['forecast'].append({ 'part_name': x['part_name'], 'temp_min': x['temp_min'], 'temp_max': x['temp_max'], 'temp_feels_like': x['feels_like'], 'condition': x['condition'], 'wind_dir': x['wind_dir'], 'wind_speed': x['wind_speed'], 'wind_gust': x['wind_gust'], 'pressure': x['pressure_mm'], 'humidity': x['humidity'], 'prec_mm': x['prec_mm'], 'prec_period': int(int(x['prec_period']) / 60), 'prec_prob': x['prec_prob'], }) return weather def get_weather(self, use_cached=True): entity, created = Service.objects.get_or_create( name=f'weather_{self.city.name}') if use_cached and not created: delta_time = (datetime.utcnow() - remove_tz(entity.update_datetime)) if delta_time.seconds < 3600 and delta_time.days == 0: weather_data = json.loads(entity.value) return weather_data weather_data = self.send_weather_request() entity.value = json.dumps(weather_data) entity.save() return weather_data
def setup_event(self, is_fwd=False): inline_query = self.raw.get('inline_query') if inline_query: self.setup_inline_query(inline_query) return if not is_fwd and self.raw.get('message', {}).get('forward_from'): self.force_response = False if is_fwd: message = self.raw else: edited_message = self.raw.get('edited_message') callback_query = self.raw.get('callback_query') my_chat_member = self.raw.get('my_chat_member') if callback_query: message = callback_query['message'] message['from'] = callback_query['from'] message['payload'] = callback_query['data'] elif edited_message: message = edited_message elif my_chat_member: message = my_chat_member else: message = self.raw.get('message') self.peer_id = message['chat']['id'] self.from_id = message['from']['id'] if message['chat']['id'] != message['from']['id']: self.chat = self.bot.get_chat_by_id(message['chat']['id']) self.is_from_chat = True else: self.is_from_pm = True _from = message['from'] if _from['is_bot']: self.is_from_bot = True else: defaults = { 'name': _from.get('first_name'), 'surname': _from.get('last_name'), 'nickname': _from.get('username'), } self.user = self.bot.get_user_by_id( _from['id'], {'nickname': _from.get('username')}) defaults.pop('nickname') self.sender = self.bot.get_profile_by_user(self.user, _defaults=defaults) self.is_from_user = True self.setup_action(message) payload = message.get('payload') if payload: self.setup_payload(payload) else: # Нет нужды парсить вложения и fwd если это просто нажатие на кнопку self.setup_attachments(message) self.setup_fwd(message.get('reply_to_message')) via_bot = message.get('via_bot') if via_bot: if via_bot['username'] == env.str("TG_BOT_LOGIN"): self.force_response = False if self.sender and self.chat: self.bot.add_chat_to_profile(self.sender, self.chat)
class TgMessage(Message): MENTION = env.str('TG_BOT_LOGIN') def __init__(self, raw_str=None, _id=None, entities=None): self._mention_entities = [] self.has_mention = False text = self.setup_message_with_entities(raw_str, entities) # save state before super has_mention = self.has_mention super().__init__(text, _id) self.has_mention = has_mention self.raw = raw_str @property def mentioned(self) -> bool: # Исключение меншона - если /command@не_наш_бот if self.has_mention: if self.MENTION not in self._mention_entities: return False return super().mentioned def setup_message_with_entities(self, text, entities): """ Находит и заменяет упоминания бота на пустоту """ if not entities: return text bot_commands = list( filter(lambda x: x['type'] in ['bot_command'], entities)) bot_commands_positions = [(x['offset'], x['length']) for x in bot_commands] mentions = list(filter(lambda x: x['type'] in ['mention'], entities)) mentions_positions = [(x['offset']) for x in mentions] if bot_commands_positions: text = self.parse_bot_command_entities(text, bot_commands_positions) elif mentions_positions: text = self.parse_mentions_entities(text, mentions_positions) return text def parse_mentions_entities(self, text, mentions_start_positions): """ Находит и заменяет упоминания бота на пустоту по mention """ tg_bot_mention = f"@{self.MENTION}".lower() text_lower = text.lower() if tg_bot_mention in text_lower: first_pos = text_lower.index(tg_bot_mention) if first_pos in mentions_start_positions: last_pos = first_pos + len(tg_bot_mention) text = text[:first_pos] + text[last_pos:] self.has_mention = True return text def parse_bot_command_entities(self, text, mentions_positions): """ Находит и заменяет упоминания бота на пустоту по bot_command """ result_text = text delete_positions = [] for offset, length in mentions_positions: start_pos = offset end_pos = start_pos + length bot_command = text[start_pos:end_pos] if '@' in bot_command: command, mention = bot_command.split("@", 1) else: command, mention = bot_command, None if mention: self.has_mention = True self._mention_entities.append(mention) if mention == self.MENTION.lower(): delete_positions.append( (start_pos + len(command), end_pos)) delete_positions = list(reversed(delete_positions)) for start_pos, end_pos in delete_positions: result_text = result_text[:start_pos] + result_text[end_pos:] return result_text
class EveryPixelAPI: IMAGE_QUALITY_URL = 'https://api.everypixel.com/v1/quality_ugc' IMAGE_FACES_URL = 'https://api.everypixel.com/v1/faces' CLIENT_ID = env.str("EVERYPIXEL_CLIENT_ID") CLIENT_SECRET = env.str("EVERYPIXEL_CLIENT_SECRET") def get_image_quality_by_url(self, url): params = { 'url': url } return requests.get(self.IMAGE_QUALITY_URL, params, auth=(self.CLIENT_ID, self.CLIENT_SECRET)).json() def get_image_quality_by_file(self, file): data = { 'data': file } return requests.post(self.IMAGE_QUALITY_URL, files=data, auth=(self.CLIENT_ID, self.CLIENT_SECRET)).json() def get_image_quality(self, url_or_bytes): if isinstance(url_or_bytes, str): response = self.get_image_quality_by_url(url_or_bytes) else: response = self.get_image_quality_by_file(url_or_bytes) if response['status'] != 'ok': raise PError("Ошибка") return f"{round(response['quality']['score'] * 100, 2)}%" def get_faces_on_photo_by_url(self, url): params = { 'url': url } return requests.get(self.IMAGE_FACES_URL, params=params, auth=(self.CLIENT_ID, self.CLIENT_SECRET)).json() def get_faces_on_photo_by_file(self, file): data = { 'data': file } return requests.post(self.IMAGE_FACES_URL, files=data, auth=(self.CLIENT_ID, self.CLIENT_SECRET)).json() def get_faces_on_photo(self, url_or_bytes): if isinstance(url_or_bytes, str): response = self.get_faces_on_photo_by_url(url_or_bytes) else: response = self.get_faces_on_photo_by_file(url_or_bytes) if response['status'] == 'error': if response['message'] == 'ratelimit exceeded 100 requests per 86400 seconds': raise PWarning("Сегодняшний лимит исчерпан") raise PError("Ошибка получения возраста на изображении") if response['status'] != "ok": raise PError("Ошибка. Статус не ок((") return response['faces']
def __init__(self): self.url = "https://geocode-maps.yandex.ru/1.x/" self.API_KEY = env.str("YANDEX_GEO_TOKEN")
class Openhab3API: URL = env.str("OPENHAB3_API_URL") TOKEN = env.str("OPENHAB3_API_TOKEN") HEADERS = { "Accept": "application/json", "Authorization": f"Bearer {TOKEN}" } def get_items(self, room=None): params = {'recursive': True} items = requests.get(f"{self.URL}/items", params, headers=self.HEADERS).json() if 'error' in items: raise PWarning("Ошибка") if room: items = list(filter(lambda x: room in x['groupNames'], items)) return self._strip_items_fields(items) @staticmethod def _strip_items_fields(items): parsed_items = [] for item in items: parsed_item = { 'name': item['name'], 'label': item['label'], 'type': item['type'], 'category': item['category'], 'sub_items': [] } sub_items = item.get('members', []) for sub_item in sub_items: parsed_sub_item = { 'name': sub_item['name'], 'label': sub_item['label'], 'type': sub_item['type'], 'state': sub_item['state'], } if sub_item.get('stateDescription'): parsed_sub_item['uom'] = sub_item['stateDescription'][ 'pattern'].replace("%s", '').replace("%%", "%") parsed_item['sub_items'].append(parsed_sub_item) parsed_items.append(parsed_item) return parsed_items def set_item_state(self, item_name, state): items = self.get_items() item = self.find_item_by_label(items, item_name) state = "ON" if state else "OFF" headers = {"Content-Type": "text/plain"} headers.update(self.HEADERS) res = requests.post(f"{self.URL}/items/{item['name']}/", data=state, headers=headers) if res.status_code != 200: raise PError("Ошибка") @staticmethod def find_item_by_label(items, label): label_lower = label.lower() for item in items: for sub_item in item['sub_items']: if sub_item['label'].lower() == label_lower: return sub_item raise PWarning(f"Не нашёл устройства с именем {label}")