radioboss-telegram-bot

Форк
0
474 строки · 20.6 Кб
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

4
# Телеграм бот для связи c RadioBoss
5
# работает на hyperadio.retroscene.org -> @hyperadio_bot
6

7
from __future__ import print_function, unicode_literals
8

9
import logging
10
import os
11
import sys
12
import requests
13
import xmltodict
14
import telegram
15
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
16

17
import sqlite3
18
import datetime
19
from sqlite3 import Error
20
import configtb
21

22
__version__ = '0.0.1'                                       # не забываем ставиь версию
23

24

25
TOKEN = configtb.token                                      # токен нашего бота
26
URL = configtb.URL                                          # URL к API телеграма
27
#PROXY_URL = 'socks5://163.172.152.192:1080'                # здесь можно поставить свой прокси
28

29
RB_PASS = configtb.rbPas                                    # пароль к API RadioBoss
30
RB_PORT = configtb.rbPort                                   # порт RadioBoss
31
ALBUM_ART_PATH = 'INSERT-HERE-PATH-TO-ALBUM-ARTWORK-FILE'   # Example 'd:\\MYRADIO\\ALBUMART\\artwork.png' путь до файла-картинки, которую выгружает RadioBoss (Albumart)
32

33
######################## текст сообщений бота ##############################
34

35
TEXT_HELP = """
36
Send me some commands:
37
/np — Get info about current playing track
38
/like — Add current track to playlist on request
39

40
/plus — Raise current track rating
41
/minus — Drop current track rating
42

43
/dl  — Download current track
44
/dln — Download track by number in current playlist
45
       Example: «/dln 1» or «/dln 25 100»
46
/art — Download album art for current track
47

48
/last — Get info about 5 last played tracks
49
/time — Get timetable
50
/help — This help
51

52
The delay for commands processing can be up to 10 seconds, so be patient, please. Do not spam me!
53
Also I can convert some chiptunes, so upload it to me ;)
54
"""
55
# стартовое сообщение
56
TEXT_START = """
57
Hi! I am a RadioBoss bot from github.com/nodeus/radioboss-telegram-bot/ (ver {:s})
58
{:s}
59
""".format(__version__, TEXT_HELP)
60
# текст расписания
61
TEXT_TIMETABLE = """
62
We broadcast 24 hours a day with some special music blocks:
63

64
08.00 - 08.30 msk 	XM tracked music
65
09.00 - 10.00 msk 	BitJam podcast
66
10.00 - 10.30 msk 	ZX Spectrum music
67
15.00 - 16.00 msk 	DEMOVIBES
68
17.00 - 17.30 msk 	ZX Spectrum music
69
18.00 - 18.30 msk 	XM tracked music
70
20.00 - 20.30 msk 	ZX Spectrum music
71
21.00 - 23.00 msk 	Music on your request
72
"""
73
# шаблон сообщения "сейчас иргает"
74
NOWPLAYNG_TPL = """
75
github.com/nodeus/radioboss-telegram-bot/
76

77
Now playing: {t_casttitle!s}
78

79
Duration: {t_duration!s}. Play position: {mins!s} min {secs!s} sec
80

81
Next track: {nt_artist!s} — {nt_title!s} ({nt_duration!s})
82
Last played: {nt_lastplayed!s}
83

84
Current listeners: {t_listeners!s}
85
"""
86
# шаблон сообщения "запрос трека"
87
TRACK_REQUEST_TPL = """
88
\U00002764 Thanks {user_name}.
89

90
Track «{t_casttitle}» added to playlist on request.
91

92
Listen to this track from 21 to 23 msk this evening.
93
"""
94
# шаблон сообщения "инфо по треку"
95
TRACK_INFO_TPL = """
96
Time (msk+2): {@LASTPLAYED}
97
Track: {@ARTIST} - {@TITLE} - {@ALBUM}
98
Playlist item №{playlist_pos}
99
"""
100
# шаблон сообщения "рейтингование"
101
RATE_TEXT_TPL = """
102
\U0001F44D Thanks {user_name}.
103
You {rate_str} the rating for «{t_casttitle}» track.
104

105
Current rating: {tag_rating} \U00002197
106
"""
107

108
# Enable logging
109
logging.basicConfig(format='%(levelname)-8s [%(asctime)s] %(message)s', level=logging.INFO, filename='mylog.log')
110
logging.root.addHandler(logging.StreamHandler(sys.stdout))
111
logger = logging.getLogger(__name__)
112

113
def radio_query(**kwargs):
114
    """функция соединения с RadioBoss"""
115
    # команда к API RadioBoss
116
    params = dict(kwargs)
117
    params['pass'] = RB_PASS
118
    response = requests.get('http://hyperadio.ru:' + RB_PORT + '/', params=params)
119
    logger.info('Request to radioboss API — %s: %s', kwargs.get('action'), response.status_code)
120
    return response
121

122
def get_username(update, context):
123
    """функция получения имени пользователя"""
124
    user_name = update.message.from_user['username']
125
    if user_name == None:
126
        user_name = update.message.from_user['first_name'] + ' ' + update.message.from_user['last_name']
127
    return user_name
128

129
def get_np():
130
    """функция отправки запроса на получение информации от RadioBoss — action playbackinfo возвращает словарь nowpl"""
131
    # команда к API RadioBoss
132

133
    r = radio_query(action='playbackinfo')
134
    info = xmltodict.parse(r.content)['Info']
135
    cur_track = info['CurrentTrack']['TRACK']
136
    next_track = info['NextTrack']['TRACK']
137
    prev_track = info['PrevTrack']['TRACK']
138
    playback = info['Playback']
139
    streaming = info['Streaming']
140
    return {
141
        't_artist': cur_track['@ARTIST'],
142
        't_title': cur_track['@TITLE'],
143
        't_album': cur_track['@ALBUM'],
144
        't_year': cur_track['@YEAR'],
145
        't_genre': cur_track['@GENRE'],
146
        't_comment': cur_track['@COMMENT'],
147
        't_filename': cur_track['@FILENAME'],
148
        't_duration': cur_track['@DURATION'],
149
        't_playcount': cur_track['@PLAYCOUNT'],
150
        't_lastplayed': cur_track['@LASTPLAYED'],
151
        't_intro': cur_track['@INTRO'],
152
        't_outro': cur_track['@OUTRO'],
153
        't_language': cur_track['@LANGUAGE'],
154
        't_f1': cur_track['@F1'],
155
        't_f2': cur_track['@F2'],
156
        't_f3': cur_track['@F3'],
157
        't_f4': cur_track['@F4'],
158
        't_f5': cur_track['@F5'],
159
        't_casttitle': cur_track['@ITEMTITLE'],
160
        't_listeners': cur_track['@LISTENERS'],
161

162
        'pt_artist': prev_track['@ARTIST'],
163
        'pt_title': prev_track['@TITLE'],
164
        'pt_album': prev_track['@ALBUM'],
165
        'pt_year': prev_track['@YEAR'],
166
        'pt_genre': prev_track['@GENRE'],
167
        'pt_comment': prev_track['@COMMENT'],
168
        'pt_filename': prev_track['@FILENAME'],
169
        'pt_duration': prev_track['@DURATION'],
170
        'pt_playcount': prev_track['@PLAYCOUNT'],
171
        'pt_lastplayed': prev_track['@LASTPLAYED'],
172
        'pt_intro': prev_track['@INTRO'],
173
        'pt_outro': prev_track['@OUTRO'],
174
        'pt_language': prev_track['@LANGUAGE'],
175
        'pt_f1': prev_track['@F1'],
176
        'pt_f2': prev_track['@F2'],
177
        'pt_f3': prev_track['@F3'],
178
        'pt_f4': prev_track['@F4'],
179
        'pt_f5': prev_track['@F5'],
180
        'pt_casttitle': prev_track['@ITEMTITLE'],
181

182
        'nt_artist': next_track['@ARTIST'],
183
        'nt_title': next_track['@TITLE'],
184
        'nt_album': next_track['@ALBUM'],
185
        'nt_year': next_track['@YEAR'],
186
        'nt_genre': next_track['@GENRE'],
187
        'nt_comment': next_track['@COMMENT'],
188
        'nt_filename': next_track['@FILENAME'],
189
        'nt_duration': next_track['@DURATION'],
190
        'nt_playcount': next_track['@PLAYCOUNT'],
191
        'nt_lastplayed': next_track['@LASTPLAYED'],
192
        'nt_intro': next_track['@INTRO'],
193
        'nt_outro': next_track['@OUTRO'],
194
        'nt_language': next_track['@LANGUAGE'],
195
        'nt_f1': next_track['@F1'],
196
        'nt_f2': next_track['@F2'],
197
        'nt_f3': next_track['@F3'],
198
        'nt_f4': next_track['@F4'],
199
        'nt_f5': next_track['@F5'],
200
        'nt_casttitle': next_track['@ITEMTITLE'],
201

202
        'play_pos': playback['@pos'],
203
        'play_len': playback['@len'],
204
        'play_state': playback['@state'],
205
        'playlist_pos': playback['@playlistpos'],
206
        'play_streams': playback['@streams'],
207
        'listeners': streaming['@listeners']
208
    }
209

210
def nowplay_string(nowpl):
211
    """создаём строку ответа для запроса /np и возвращаем её"""
212
    secs = int(nowpl['play_pos']) // 1000  # считаем минуты / секунды
213
    mins = secs // 60
214
    secs = secs - mins * 60
215
    return NOWPLAYNG_TPL.format(mins=mins, secs=secs, **nowpl)
216

217
def request_song(user_name):
218
    """функция добавления трека в плейлист заказа"""
219
    nowpl = get_np()
220
    radio_query(action='songrequest', filename=nowpl['t_filename'], message=user_name)
221
    return None
222

223
def start(update, context):
224
    """отправляем сообщение приветствия когда команда /start запрошена"""
225
    update.message.reply_text(TEXT_START)
226
    user_name = get_username(update, context)
227
    logger.info('--- %s start interaction with bot ---', user_name)
228

229
def helpme(update, context):
230
    """отправляем сообщение помощи когда команда /help запрошена"""
231
    update.message.reply_text(TEXT_HELP)
232
    user_name = get_username(update, context)
233
    logger.info('%s request help', user_name)
234

235
def dl_track(update, context):
236
    """отправляем текущий трек когда команда /dl запрошена"""
237
    # TODO сделать проверку на уже отправленные файлы в телеграм и отдавать ссылкой на telegram-id файла, если уже были закачаны
238
    # нужна база отправленных файлов
239
    nowpl = get_np()
240
    title = str(nowpl['t_casttitle'])
241
    filename = nowpl['t_filename']
242
    context.bot.send_chat_action(chat_id=update.message.chat_id, action=telegram.ChatAction.UPLOAD_DOCUMENT)
243
    context.bot.send_audio(timeout=120, caption=title, chat_id=update.message.chat_id, audio=open(filename, 'rb'))
244
    user_name = get_username(update, context)
245
    logger.info('%s download %s', user_name, filename)
246

247
def dl_number(update, context):
248
    """отправляем трек из базы по запрошенному номеру с текущего плейлиста"""
249
    # TODO сделать проверку на уже отправленные файлы в телеграм и отдавать ссылкой на telegram-id файла, если уже были закачаны
250
    # нужна база отправленных файлов
251
    user_name = get_username(update, context)
252
    if not context.args:
253
        update.message.reply_text('Please, type track numbers after command.\nExample: «/dln 1 2 3»')
254
        logger.info('%s use /dln command without args.', user_name)
255
    else:
256
        for track_number in context.args:
257
            track_number.strip(", ")
258
            if track_number.isdigit():
259
                response = radio_query(action='trackinfo', pos=track_number)
260
                try:
261
                    trinfo = xmltodict.parse(response.content)
262
                    track = trinfo['Info']['Track']['TRACK']
263
                    file_name = track['@FILENAME']
264
                    track_title = track['@ARTIST'] + ' — ' + track['@TITLE']
265
                    context.bot.send_chat_action(chat_id=update.message.chat_id, action=telegram.ChatAction.UPLOAD_DOCUMENT)
266
                    context.bot.send_document(timeout=120, filename=file_name, caption=track_title,
267
                                      chat_id=update.message.chat_id, document=open(file_name, 'rb'))
268

269
                    logger.info('%s download track №%s file: %s', user_name, track_number, file_name)
270
                except Exception as e:
271
                    logger.info('Wrong track number %s', track_number, '\n', file_name)
272
                    update.message.reply_text('Wrong track number {!s}. Please try again.'.format(track_number))
273
            else:
274
                update.message.reply_text(track_number + '%s — isn`t number of track i know...')
275
                logger.info('%s type wrong track number — %s', user_name, track_number)
276

277
def dl_art(update, context):
278
    """отправляем обложку трека/альбома когда команда /art запрошена"""
279
    user_name = get_username(update, context)
280
    nowpl = get_np()
281
    if os.path.exists(ALBUM_ART_PATH):
282
        context.bot.send_photo(chat_id=update.message.chat_id, photo=open(ALBUM_ART_PATH, 'rb'))
283
        logger.info('%s download %s album art.', user_name, nowpl['t_filename'])
284
    else:
285
        update.message.reply_text('Sorry, no album art for this track.')
286
        logger.info('%s request %s album art, but it is not found.', user_name, nowpl['t_filename'])
287

288
def np(update, context):
289
    """отправляем nowplay с сервера RadioBoss в телеграм"""
290
    nowpl = get_np()
291
    update.message.reply_text(nowplay_string(nowpl))
292
    user_name = get_username(update, context)
293
    logger.info('%s request Nowplay for %s', user_name, nowpl['t_casttitle'])
294

295
def like(update, context):
296
    """отправляем like на сервер radioboss и сообщение в телеграм"""
297
    user_name = get_username(update, context)
298
    nowpl = get_np()
299
    request_song(user_name)
300
    update.message.reply_text(TRACK_REQUEST_TPL.format(user_name=user_name, **nowpl))
301
    logger.info('%s liked %s', user_name, nowpl['t_casttitle'])
302

303
def timetable(update, context):
304
    """отправляем расписание в телеграм"""
305
    update.message.reply_text(TEXT_TIMETABLE)
306
    user_name = get_username(update, context)
307
    logger.info('%s request timetable', user_name)
308

309
def last(update, context):
310
    """отправляем информацию по 5 последним проигранным трекам"""
311
    user_name = get_username(update, context)
312
    nowpl = get_np()
313
    infopos = int(nowpl['playlist_pos'])
314

315
    for x in range(0, min(infopos, 5)):
316
        response = radio_query(action='trackinfo', pos=str(infopos - x))
317
        trinfo = xmltodict.parse(response.content)
318
        track_info = trinfo['Info']['Track']['TRACK']
319
        update.message.reply_text(TRACK_INFO_TPL.format(playlist_pos=infopos - x, **track_info))
320

321
    logger.info('%s request last played', user_name)
322
    update.message.reply_text('Nowplay: ' + nowpl['t_casttitle'] + '\nPlaylist item №: ' + nowpl['playlist_pos'])
323

324
def error(update, context):
325
    """логгируем ошибки и отправляем сообщение в телеграм, если что-то пошло не так"""
326
    logger.warning('Update "%s" caused error "%s"', update, context.error)
327
    update.message.reply_text('Ooops, something went wrong. Sorry...')
328

329
# соединение с бд sqlite
330
def sql_connection():
331
    try:
332
        con = sqlite3.connect('rating.db')
333
        print ("Connection is established")
334
        logger.info('Connection is established')
335
    except Error:
336
        print(Error)
337
        logger.info(Error)
338
    finally:
339
        con.close()
340
        logger.info('connection is closed')
341

342
def sql_insert(con, entities):
343
    """добавляем в таблицу id пользователя, имя пользователя, название трека, дату голосования"""
344
    cursor = con.cursor()
345
    cursor.execute('INSERT INTO rating (userid, username, ratedtrack, ratedate) VALUES(?,?,?,?)', entities)
346
    con.commit()
347

348
def sql_fetch(con,user_id,rated_track):
349
    """возвращаем дату голосования по имени пользователя и названию трека"""
350
    cursor = con.cursor()
351
    cursor.execute('SELECT ratedate FROM rating WHERE userid = :uid AND ratedtrack = :rtrack', {'uid': user_id, 'rtrack': rated_track})
352
    row = cursor.fetchone()
353
    return row
354

355
def change_rating(update, context):
356
    """изменение рейтинга трека"""
357
    try:
358
        # запрос информации от hyperadio сервера
359
        nowpl = get_np()
360

361
        # имя пользователя запроса
362
        user_name = get_username(update, context)
363

364
        # id пользователя запроса
365
        user_id = update.message.from_user['id']
366

367
        # текущая дата
368
        rate_date = datetime.date.today()
369

370
        tagxml = radio_query(action='readtag', fn=nowpl['t_filename'])
371
        tagdoc = xmltodict.parse(tagxml.content)
372

373
        file = tagdoc['TagInfo']['File']
374
        taginfo = {'tag_filename': file['@FN'],
375
                   'tag_duration': file['@Duration'],
376
                   'tag_artist': file['@Artist'],
377
                   'tag_title': file['@Title'],
378
                   'tag_album': file['@Album'],
379
                   'tag_year': file['@Year'],
380
                   'tag_genre': file['@Genre'],
381
                   'tag_comment': file['@Comment'],
382
                   'tag_bpm': file['@BPM'],
383
                   'tag_rating': file['@Rating'],
384
                   'tag_playcount': file['@Playcount'],
385
                   'tag_lastplayed': file['@LastPlayed']}
386

387
        # полный путь запрошенного файла
388
        rated_track = taginfo['tag_filename']
389

390
        # рейтинг запрошенного файла
391
        rating = int(taginfo['tag_rating'])
392

393
        if context.direction == 1 and rating == 10:
394
            update.message.reply_text('This track has the highest rating — 10.')
395
            return
396
        elif context.direction == -1 and rating == 0:
397
            update.message.reply_text('This track has the lowest rating — 0.')
398
            return
399

400
        # подключаемся к базе
401
        con = sqlite3.connect('rating.db')
402
        sql_connection()
403

404
        # запрос из базы, если совпадение с текущим id пользователя и именем файла
405
        # получаем None — нет совпадений, или дату — есть совпадение
406
        get_date = sql_fetch(con,user_id,rated_track)
407
        
408
        if get_date is None:
409
            rating = max(min(rating + context.direction, 10), 0)
410
            taginfo['tag_rating'] = str(rating)
411
            rate_str = 'increased' if context.direction == 1 else 'dropped'
412
            update.message.reply_text(RATE_TEXT_TPL.format(user_name=user_name, rate_str=rate_str, tag_rating=rating, **nowpl))
413
            file['@Rating'] = str(rating)
414
            newxml = xmltodict.unparse(tagdoc)
415
            radio_query(action='writetag', fn=taginfo['tag_filename'], data=newxml)
416
            logger.info('%s %s the rating for %s — %s to %s', user_name, rate_str, taginfo['tag_artist'], taginfo['tag_title'], rating)
417

418
            # данные для записи в базу
419
            entities = (user_id, user_name, rated_track, rate_date)
420
            # пишем в базу
421
            sql_insert(con,entities)
422
            return
423
        else:
424
            update.message.reply_text('Sorry, you can not vote for this track twice...\nRating for «' + taginfo['tag_artist'] + ' – ' + taginfo['tag_title'] + '» has been changed by you at: ' + get_date[0])
425
            logger.info('%s tried to voting twice for %s – %s.', user_name, taginfo['tag_artist'], taginfo['tag_title'] )
426
            return
427
            
428
    except Exception as e:
429
        logger.exception(e)
430

431
def ratingplus(update, context):
432
    """добавление 1 к рейтингу текущего трека"""
433
    context.direction = 1
434
    return change_rating(update, context)
435

436
def ratingminus(update, context):
437
    """вычитание 1 из рейтинга текущего трека"""
438
    context.direction = -1
439
    return change_rating(update, context)
440

441
def main():
442
    """запуск бота"""
443
    # раскомментировать если используется прокси
444
    #if PROXY_URL:
445
    #    request_kwargs = {'proxy_url': PROXY_URL}
446
    #else:
447
    request_kwargs = {}
448

449
    updater = Updater(configtb.token, use_context=True)
450
    dp = updater.dispatcher
451

452
    # команды, обрабатываемые ботом
453
    dp.add_handler(CommandHandler("start", start))
454
    dp.add_handler(CommandHandler("help", helpme))
455
    dp.add_handler(CommandHandler("like", like))
456
    dp.add_handler(CommandHandler("plus", ratingplus))
457
    dp.add_handler(CommandHandler("minus", ratingminus))
458
    dp.add_handler(CommandHandler("np", np))
459
    dp.add_handler(CommandHandler("dl", dl_track))
460
    dp.add_handler(CommandHandler("dln", dl_number, pass_args=True))
461
    dp.add_handler(CommandHandler("art", dl_art))
462
    dp.add_handler(CommandHandler("last", last))
463
    dp.add_handler(CommandHandler("time", timetable))
464
    
465
    # логгирование ошибок
466
    dp.add_error_handler(error)
467

468
    # старт бота
469
    updater.start_polling(poll_interval=2.0, timeout=10000)
470

471
    updater.idle()
472

473
if __name__ == '__main__':
474
    main()
475

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.