2017-11-11 17:55:13 +00:00
import random
2018-02-26 15:38:47 +00:00
import re
2019-01-02 19:51:43 +00:00
# discord.py has a different name
# noinspection PyPackageRequirements
2017-10-27 11:38:32 +00:00
import discord
2019-01-02 19:51:43 +00:00
# noinspection PyPackageRequirements
2017-10-31 22:50:05 +00:00
import discord . opus
2019-01-02 19:51:43 +00:00
# noinspection PyPackageRequirements
2018-02-25 18:50:57 +00:00
import discord . voice_client
2017-11-01 18:23:11 +00:00
import functools
2017-11-09 19:34:18 +00:00
import sys
2017-10-27 11:38:32 +00:00
import db
2017-11-05 21:50:37 +00:00
import youtube_dl
2018-01-15 12:42:40 +00:00
import concurrent . futures
2018-02-25 18:50:57 +00:00
import typing
2018-02-25 19:47:12 +00:00
import os
2018-02-26 09:48:17 +00:00
import asyncio
import configparser
2018-04-12 16:04:13 +00:00
import raven
2018-05-31 19:43:43 +00:00
import logging
2018-07-26 17:26:03 +00:00
import datetime
2018-09-17 22:26:01 +00:00
import coloredlogs
2019-04-11 15:58:46 +00:00
from utils import errors
2018-11-28 14:47:08 +00:00
import math
2018-12-03 11:00:37 +00:00
import enum
2019-01-04 17:39:48 +00:00
import requests
2018-05-31 19:43:43 +00:00
2018-09-18 22:02:39 +00:00
logging . getLogger ( ) . disabled = True
2018-08-16 17:46:07 +00:00
logger = logging . getLogger ( __name__ )
2018-09-18 22:13:41 +00:00
os . environ [ " COLOREDLOGS_LOG_FORMAT " ] = " %(asctime)s %(levelname)s %(name)s %(message)s "
2018-09-17 22:26:01 +00:00
coloredlogs . install ( level = " DEBUG " , logger = logger )
2017-10-27 11:38:32 +00:00
2019-01-04 23:45:01 +00:00
# Number emojis from zero (now playing) to ten
number_emojis = [ " :arrow_forward: " ,
" :one: " ,
2019-01-02 19:33:15 +00:00
" :two: " ,
" :three: " ,
" :four: " ,
" :five: " ,
" :six: " ,
" :seven: " ,
" :eight: " ,
" :nine: " ,
" :keycap_ten: " ]
2018-02-26 15:38:47 +00:00
2017-10-27 11:38:32 +00:00
# Init the event loop
loop = asyncio . get_event_loop ( )
2018-08-07 23:30:43 +00:00
# FFmpeg settings
ffmpeg_settings = { }
2018-08-08 12:09:57 +00:00
# Init the executor
executor = concurrent . futures . ThreadPoolExecutor ( max_workers = 3 )
2018-11-26 14:45:13 +00:00
class Succ :
""" All calls to this class return itself. """
def __bool__ ( self ) :
return False
2018-12-04 14:21:09 +00:00
2018-12-03 23:56:29 +00:00
def __getattr__ ( self , attr ) :
2018-11-26 14:45:13 +00:00
return Succ ( )
def __call__ ( self , * args , * * kwargs ) :
return Succ ( )
def __str__ ( self ) :
return " succ "
def __repr__ ( self ) :
return " <Succ> "
2018-05-26 08:50:54 +00:00
class Video :
2018-12-04 14:21:09 +00:00
def __init__ ( self , enqueuer : typing . Optional [ discord . Member ] = None ) :
2018-12-03 11:00:37 +00:00
self . is_ready = False
self . name = None
2018-07-25 22:08:53 +00:00
self . enqueuer = enqueuer
2018-12-03 11:00:37 +00:00
self . audio_source = None
2019-01-04 18:33:00 +00:00
self . suggestion = None
2018-05-26 08:50:54 +00:00
def __str__ ( self ) :
2018-12-03 15:53:03 +00:00
if self . name is None :
return " _Untitled_ "
2018-12-03 11:00:37 +00:00
return self . name
def plain_text ( self ) :
""" Title without formatting to be printed on terminals. """
2018-12-03 15:53:03 +00:00
if self . name is None :
return " Untitled "
2018-12-03 11:00:37 +00:00
return self . name
def database_text ( self ) :
""" The text to be stored in the database for the stats. Usually the same as plain_text(). """
2018-12-03 15:53:03 +00:00
if self . name is None :
raise errors . VideoHasNoName ( )
2018-12-03 11:00:37 +00:00
return self . name
2018-05-27 13:23:33 +00:00
2018-09-17 22:19:35 +00:00
def __repr__ ( self ) :
2018-12-03 11:00:37 +00:00
return f " <Video { self . name } ( { ' ' if self . is_ready else ' not ' } ready) added by { self . enqueuer } > "
def ready_up ( self ) :
""" Prepare the video for playback in some way. For example, download it. """
raise NotImplementedError ( )
def make_audio_source ( self ) :
""" Create an AudioSource to be played through Discord, and store and return it. """
raise NotImplementedError ( )
2018-12-04 14:21:09 +00:00
2019-01-04 18:33:00 +00:00
def get_suggestion ( self ) :
""" Set the next suggested video in the self.suggestion variable, to be used when the queue is in
LoopMode . FOLLOW_SUGGESTION """
2018-12-03 11:00:37 +00:00
raise NotImplementedError ( )
class YoutubeDLVideo ( Video ) :
""" A file sourcing from YoutubeDL. """
2018-12-04 14:21:09 +00:00
2019-01-04 17:39:48 +00:00
def __init__ ( self , url , enqueuer : typing . Optional [ discord . Member ] = None , * , youtube_api_key : str = None ) :
2018-12-03 11:00:37 +00:00
super ( ) . __init__ ( enqueuer )
self . url = url
self . info = None
2019-01-04 17:39:48 +00:00
self . youtube_api_key = youtube_api_key
2018-12-03 11:00:37 +00:00
def get_info ( self ) :
""" Get info about the video. """
2018-12-03 15:53:03 +00:00
if self . info :
return
2019-01-04 23:45:01 +00:00
logger . debug ( f " Getting info on { self . url } ... " )
2018-12-03 15:53:03 +00:00
with youtube_dl . YoutubeDL ( { " quiet " : True ,
" ignoreerrors " : True ,
" simulate " : True } ) as ytdl :
data = ytdl . extract_info ( url = self . url , download = False )
if data is None :
raise errors . VideoInfoExtractionFailed ( )
2018-12-03 23:56:29 +00:00
if " entries " in data :
2018-12-03 15:53:03 +00:00
raise errors . VideoIsPlaylist ( )
self . info = data
self . name = data . get ( " title " )
2018-12-03 11:00:37 +00:00
2018-12-03 15:53:03 +00:00
def __str__ ( self ) :
if self . info is None :
return f " ` { self . url } ` "
return f " _ { self . name } _ "
2018-09-17 22:19:35 +00:00
2018-05-29 21:50:47 +00:00
def plain_text ( self ) :
2018-12-03 15:53:03 +00:00
if self . info is None :
return self . url
if not self . name . isprintable ( ) :
return self . url
return self . name
2018-12-03 11:00:37 +00:00
def get_filename ( self ) :
""" Generate the filename of the video. """
2018-12-03 23:56:29 +00:00
if self . info is None :
2018-12-03 15:53:03 +00:00
raise errors . VideoInfoUnknown ( )
2018-12-03 23:56:29 +00:00
return " ./opusfiles/ {} .opus " . format ( re . sub ( r ' [/ \\ ?* " <>|!:] ' , " _ " , self . info . get ( " title " , self . info [ " id " ] ) ) )
2018-12-04 14:21:09 +00:00
2018-12-03 15:53:03 +00:00
def ready_up ( self ) :
""" Download the video. """
# Skip download if it is already ready
if self . is_ready :
return
# Retrieve info about the video
self . get_info ( )
# Check if the file to download already exists
if os . path . exists ( self . get_filename ( ) ) :
self . is_ready = True
return
2018-05-27 13:23:33 +00:00
# Download the file
2018-12-03 15:53:03 +00:00
logger . info ( f " Starting youtube_dl download of { repr ( self ) } " )
2018-05-27 13:23:33 +00:00
with youtube_dl . YoutubeDL ( { " noplaylist " : True ,
" format " : " best " ,
" postprocessors " : [ {
" key " : ' FFmpegExtractAudio ' ,
2018-08-18 15:36:44 +00:00
" preferredcodec " : ' opus '
2018-05-27 13:23:33 +00:00
} ] ,
2018-12-03 15:53:03 +00:00
" outtmpl " : self . get_filename ( ) ,
2018-05-27 13:23:33 +00:00
" quiet " : True } ) as ytdl :
2018-12-17 00:51:32 +00:00
ytdl . download ( [ self . url ] )
2018-12-03 15:53:03 +00:00
logger . info ( f " Completed youtube_dl download of { repr ( self ) } " )
self . is_ready = True
2018-05-27 13:23:33 +00:00
2018-12-03 15:53:03 +00:00
def make_audio_source ( self ) :
if not self . is_ready :
raise errors . VideoIsNotReady ( )
self . audio_source = discord . PCMVolumeTransformer ( discord . FFmpegPCMAudio ( self . get_filename ( ) , * * ffmpeg_settings ) )
return self . audio_source
2018-08-07 23:30:43 +00:00
2019-01-04 18:33:00 +00:00
def get_suggestion ( self ) :
if self . suggestion is not None :
return
# Ensure the video has info
self . get_info ( )
2019-01-04 23:45:01 +00:00
# Ensure the video is from youtube
2019-01-04 17:39:48 +00:00
if self . info [ " extractor " ] != " youtube " :
# TODO: add more websites?
2019-01-04 18:33:00 +00:00
self . suggestion = NotImplemented
2019-01-04 23:45:01 +00:00
# Log the attempt
logger . debug ( f " Getting a suggestion for { self . url } ... " )
# Check for the api key
2019-01-04 17:39:48 +00:00
if self . youtube_api_key is None :
raise errors . MissingAPIKeyError ( )
# Request search data (costs 100 API units)
r = requests . get ( " https://www.googleapis.com/youtube/v3/search?part=snippet " , params = {
" part " : " snippet " ,
" type " : " video " ,
" key " : self . youtube_api_key ,
" relatedToVideoId " : self . info [ " id " ]
} )
r . raise_for_status ( )
# Parse the request data
j = r . json ( )
# Find the suggested video
if len ( j [ " items " ] ) < 1 :
2019-01-04 18:33:00 +00:00
self . suggestion = . . .
2019-01-04 17:39:48 +00:00
first_video_id = j [ " items " ] [ 0 ] [ " id " ] [ " videoId " ]
2019-01-04 18:33:00 +00:00
self . suggestion = YoutubeDLVideo ( f " https://www.youtube.com/watch?v= { first_video_id } " ,
enqueuer = None ,
youtube_api_key = self . youtube_api_key )
2019-01-04 16:59:00 +00:00
2018-08-07 23:30:43 +00:00
2018-12-03 11:00:37 +00:00
class LoopMode ( enum . Enum ) :
NORMAL = enum . auto ( )
LOOP_QUEUE = enum . auto ( )
LOOP_SINGLE = enum . auto ( )
FOLLOW_SUGGESTIONS = enum . auto ( )
2018-12-04 10:18:00 +00:00
AUTO_SHUFFLE = enum . auto ( )
LOOPING_SHUFFLE = enum . auto ( )
2018-10-10 17:27:24 +00:00
2018-12-04 16:55:43 +00:00
class VideoQueue :
2018-11-28 15:44:34 +00:00
""" The queue of videos to be played. """
def __init__ ( self ) :
self . list : typing . List [ Video ] = [ ]
2018-12-03 11:00:37 +00:00
self . loop_mode = LoopMode . NORMAL
2018-10-10 17:27:24 +00:00
2018-11-28 15:44:34 +00:00
def __len__ ( self ) - > int :
return len ( self . list )
2018-12-03 11:00:37 +00:00
def __repr__ ( self ) - > str :
2019-01-04 23:45:01 +00:00
return f " <VideoQueue of length { len ( self ) } in mode { self . loop_mode } > "
2018-12-03 11:00:37 +00:00
2018-12-04 14:21:09 +00:00
def add ( self , video : Video , position : int = None ) - > None :
2018-11-28 15:44:34 +00:00
if position is None :
self . list . append ( video )
return
self . list . insert ( position , video )
2018-12-04 14:21:09 +00:00
2019-01-04 23:45:01 +00:00
def pop ( self , index ) - > Video :
if self . loop_mode == LoopMode . FOLLOW_SUGGESTIONS :
raise errors . LoopModeError ( " Can ' t pop items from a suggestion queue. " )
result = self [ index ]
self . list . remove ( result )
return result
2018-12-03 11:00:37 +00:00
def advance_queue ( self ) :
2018-12-04 10:18:00 +00:00
""" Advance the queue to the next video. """
2019-01-04 23:45:01 +00:00
del self [ 0 ]
2018-12-03 11:00:37 +00:00
2018-11-28 15:44:34 +00:00
def next_video ( self ) - > typing . Optional [ Video ] :
2019-01-04 23:45:01 +00:00
try :
return self [ 1 ]
except IndexError :
2018-11-28 15:44:34 +00:00
return None
def shuffle ( self ) :
2019-01-04 23:45:01 +00:00
part = self . list [ 1 : ]
random . shuffle ( part )
part . insert ( 0 , self . list [ 0 ] )
self . list = part
2018-11-28 15:44:34 +00:00
def clear ( self ) :
2018-12-06 23:04:26 +00:00
self . list = [ ]
2018-11-28 15:44:34 +00:00
2018-12-03 15:53:03 +00:00
def find_video ( self , name : str ) - > typing . Optional [ Video ] :
""" Returns the first video with a certain name. """
2018-11-28 15:44:34 +00:00
for video in self . list :
2018-12-03 15:53:03 +00:00
if name in video . name :
2018-11-28 15:44:34 +00:00
return video
return None
2018-12-03 11:00:37 +00:00
2018-12-04 14:21:09 +00:00
def not_ready_videos ( self , limit : typing . Optional [ int ] = None ) :
2018-12-03 15:53:03 +00:00
""" Return the non-ready videos in the first limit positions of the queue. """
2018-12-04 14:21:09 +00:00
video_list = [ ]
2019-01-04 23:45:01 +00:00
for video in ( self [ : limit ] ) :
2019-01-04 18:33:00 +00:00
if not video . is_ready :
video_list . append ( video )
2019-01-04 23:45:01 +00:00
return video_list
async def async_not_ready_videos ( self , limit : typing . Optional [ int ] = None ) :
""" Return the non-ready videos in the first limit positions of the queue. """
video_list = [ ]
full_list = await loop . run_in_executor ( executor , functools . partial ( self . __getitem__ , slice ( None , limit , None ) ) )
for video in full_list :
2018-12-03 15:53:03 +00:00
if not video . is_ready :
2018-12-04 14:21:09 +00:00
video_list . append ( video )
return video_list
2019-01-04 23:45:01 +00:00
def __getitem__ ( self , index : typing . Union [ int , slice ] ) - > typing . Union [ Video , typing . Iterable ] :
""" Get an enqueued element. """
if isinstance ( index , int ) :
if self . loop_mode == LoopMode . NORMAL :
return self . list [ index ]
elif self . loop_mode == LoopMode . LOOP_QUEUE :
if len ( self . list ) == 0 :
raise IndexError ( )
return self . list [ index % len ( self . list ) ]
elif self . loop_mode == LoopMode . LOOP_SINGLE :
if len ( self . list ) == 0 :
raise IndexError ( )
return self . list [ 0 ]
elif self . loop_mode == LoopMode . FOLLOW_SUGGESTIONS :
counter = index
video = self . list [ 0 ]
while counter > 0 :
video . get_suggestion ( )
video = video . suggestion
counter - = 1
return video
elif self . loop_mode == LoopMode . AUTO_SHUFFLE :
if index == 0 :
return self . list [ 0 ]
elif index > = len ( self . list ) :
raise IndexError ( )
number = random . randrange ( 1 , len ( self . list ) )
return self . list [ number ]
elif self . loop_mode == LoopMode . LOOPING_SHUFFLE :
if index == 0 :
return self . list [ 0 ]
number = random . randrange ( 1 , len ( self . list ) )
return self . list [ number ]
else :
# FIXME: won't work properly in multiple cases, but should work fine enough for now
video_list = [ ]
if self . loop_mode == LoopMode . FOLLOW_SUGGESTIONS :
try :
video = self . list [ 0 ]
except IndexError :
return video_list
while True :
if video is None :
break
video_list . append ( video )
video = video . suggestion
else :
for i in range ( len ( self ) ) :
video_list . append ( self [ i ] )
return video_list [ index ]
async def async_getitem ( self , index ) :
return await loop . run_in_executor ( executor , functools . partial ( self . __getitem__ , index ) )
def __delitem__ ( self , index : typing . Union [ int , slice ] ) :
if isinstance ( index , int ) :
if self . loop_mode == LoopMode . LOOP_SINGLE :
pass
elif self . loop_mode == LoopMode . FOLLOW_SUGGESTIONS :
if index != 0 :
raise errors . LoopModeError ( " Deleting suggested videos different than the current one is impossible. " )
else :
video = self [ 0 ]
if video . suggestion is not None :
self . list . append ( video . suggestion )
del self . list [ 0 ]
del self . list [ index ]
else :
for i in range ( index . start , index . stop , index . step ) :
del self [ i ]
2018-10-10 17:27:24 +00:00
2018-11-07 19:50:55 +00:00
def escape ( message : str ) :
return message . replace ( " < " , " < " ) . replace ( " > " , " > " )
2018-08-08 12:09:57 +00:00
def command ( func ) :
""" Decorator. Runs the function as a Discord command. """
async def new_func ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] , * args ,
* * kwargs ) :
if author is not None :
2019-01-02 19:21:06 +00:00
self . sentry . user_context ( {
2018-08-08 12:09:57 +00:00
" discord_id " : author . id ,
" username " : f " { author . name } # { author . discriminator } "
} )
else :
2019-01-02 19:21:06 +00:00
self . sentry . user_context ( {
2018-08-08 12:09:57 +00:00
" source " : " Telegram "
} )
try :
result = await func ( self , channel = channel , author = author , params = params , * args , * * kwargs )
except Exception :
ei = sys . exc_info ( )
2018-09-17 22:07:00 +00:00
# noinspection PyBroadException
2018-08-08 12:09:57 +00:00
try :
await channel . send ( f " ☢ **ERRORE DURANTE L ' ESECUZIONE DEL COMANDO { params [ 0 ] } ** \n "
f " Il comando è stato ignorato. \n "
f " Una segnalazione di errore è stata automaticamente mandata a Steffo. \n \n "
f " Dettagli dell ' errore: \n "
f " ```python \n "
f " { repr ( ei [ 1 ] ) } \n "
f " ``` " )
except Exception :
pass
2019-01-02 19:21:06 +00:00
self . sentry . captureException ( exc_info = ei )
2018-08-08 12:09:57 +00:00
else :
return result
return new_func
def requires_connected_voice_client ( func ) :
""" Decorator. Ensures the voice client is connected before running the command. """
async def new_func ( self : " RoyalDiscordBot " , channel : discord . TextChannel , author : discord . Member ,
params : typing . List [ str ] , * args , * * kwargs ) :
for voice_client in self . voice_clients :
if voice_client . channel in self . main_guild . channels and voice_client . is_connected ( ) :
break
else :
await channel . send ( " ⚠️ Non sono connesso alla cv! \n "
" Fammi entrare scrivendo `!cv` mentre sei in chat vocale. " )
return
return await func ( self , channel = channel , author = author , params = params , * args , * * kwargs )
return new_func
def requires_rygdb ( func , optional = False ) :
async def new_func ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] , * args ,
* * kwargs ) :
session = db . Session ( )
dbuser = await loop . run_in_executor ( executor ,
session . query ( db . Discord )
. filter_by ( discord_id = author . id )
. join ( db . Royal )
. first )
await loop . run_in_executor ( executor , session . close )
if not optional and dbuser is None :
await channel . send ( " ⚠️ Devi essere registrato su Royalnet per poter utilizzare questo comando. " )
return
return await func ( self , channel = channel , author = author , params = params , dbuser = dbuser , * args , * * kwargs )
return new_func
2018-08-07 23:30:43 +00:00
class RoyalDiscordBot ( discord . Client ) :
2019-01-04 17:44:03 +00:00
def __init__ ( self , * args , * * kwargs ) :
2018-08-07 23:30:43 +00:00
super ( ) . __init__ ( * args , * * kwargs )
self . main_channel : typing . Optional [ discord . TextChannel ] = None
self . main_guild : typing . Optional [ discord . Guild ] = None
2018-11-28 15:44:34 +00:00
self . video_queue : VideoQueue = VideoQueue ( )
2018-11-28 14:47:08 +00:00
self . load_config ( " config.ini " )
2019-01-04 17:44:03 +00:00
self . commands = {
f " { self . command_prefix } ping " : self . cmd_ping ,
f " { self . command_prefix } cv " : self . cmd_cv ,
f " { self . command_prefix } summon " : self . cmd_cv ,
f " { self . command_prefix } play " : self . cmd_play ,
f " { self . command_prefix } alexaplay " : self . cmd_play ,
f " { self . command_prefix } okgoogleplay " : self . cmd_play ,
f " { self . command_prefix } heysiriplay " : self . cmd_play ,
f " { self . command_prefix } p " : self . cmd_play ,
f " { self . command_prefix } search " : self . cmd_play ,
f " { self . command_prefix } file " : self . cmd_play ,
f " { self . command_prefix } skip " : self . cmd_skip ,
f " { self . command_prefix } s " : self . cmd_skip ,
f " { self . command_prefix } next " : self . cmd_skip ,
f " { self . command_prefix } remove " : self . cmd_remove ,
f " { self . command_prefix } r " : self . cmd_remove ,
f " { self . command_prefix } cancel " : self . cmd_remove ,
f " { self . command_prefix } queue " : self . cmd_queue ,
f " { self . command_prefix } q " : self . cmd_queue ,
f " { self . command_prefix } shuffle " : self . cmd_shuffle ,
f " { self . command_prefix } clear " : self . cmd_clear ,
f " { self . command_prefix } register " : self . cmd_register ,
f " { self . command_prefix } radiomessages " : self . cmd_radiomessages ,
f " { self . command_prefix } yes " : self . null ,
f " { self . command_prefix } no " : self . null ,
f " { self . command_prefix } pause " : self . cmd_pause ,
f " { self . command_prefix } resume " : self . cmd_resume ,
f " { self . command_prefix } loop " : self . cmd_mode ,
f " { self . command_prefix } l " : self . cmd_mode ,
f " { self . command_prefix } mode " : self . cmd_mode ,
f " { self . command_prefix } m " : self . cmd_mode
}
2019-01-02 19:21:06 +00:00
if self . sentry_token :
self . sentry = raven . Client ( self . sentry_token ,
release = raven . fetch_git_sha ( os . path . dirname ( __file__ ) ) ,
install_logging_hook = False ,
hook_libraries = [ ] )
else :
logger . warning ( " Sentry not set, ignoring all calls to it. " )
self . sentry = Succ ( )
2018-09-14 23:05:19 +00:00
self . inactivity_timer = 0
2018-08-07 23:30:43 +00:00
2018-12-03 23:56:29 +00:00
# noinspection PyAttributeOutsideInit
2018-11-28 14:47:08 +00:00
def load_config ( self , filename ) :
2018-12-03 23:56:29 +00:00
# Init the config reader
config = configparser . ConfigParser ( )
2019-01-02 19:33:35 +00:00
config . read ( filename )
2018-12-03 23:56:29 +00:00
# Token
try :
self . token = config [ " Discord " ] [ " bot_token " ]
except ( KeyError , ValueError ) :
raise errors . InvalidConfigError ( " Missing Discord bot token. " )
# Main channels, will be fully loaded when ready
try :
self . main_guild_id = int ( config [ " Discord " ] [ " server_id " ] )
self . main_channel_id = int ( config [ " Discord " ] [ " main_channel " ] )
except ( KeyError , ValueError ) :
raise errors . InvalidConfigError ( " Missing main guild and channel ids. " )
# Max enqueable video duration
# Defined in the YoutubeDLVideo class
# Max videos to predownload
try :
2019-01-03 13:17:44 +00:00
self . max_videos_to_predownload = int ( config [ " Discord " ] [ " video_cache_size " ] )
2018-12-03 23:56:29 +00:00
except ( KeyError , ValueError ) :
logger . warning ( " Max videos to predownload is not set, setting it to infinity. " )
self . max_videos_to_predownload = None
# Max time to ready a video
try :
2019-01-03 13:17:44 +00:00
self . max_video_ready_time = int ( config [ " Discord " ] [ " max_ready_time " ] )
2018-12-03 23:56:29 +00:00
except ( KeyError , ValueError ) :
logger . warning ( " Max time to ready a video is not set, setting it to infinity. " )
self . max_video_ready_time = math . inf
# Radio messages
try :
2019-01-02 19:33:15 +00:00
if config [ " Discord " ] [ " radio_messages_enabled " ] == " True " :
self . radio_messages = [ " https://www.youtube.com/watch?v=3-yeK1Ck4yk " ,
" https://youtu.be/YcR7du_A1Vc " ,
" https://clyp.it/byg3i52l " ]
try :
self . radio_messages_every = int ( config [ " Discord " ] [ " radio_messages_every " ] )
self . radio_messages_next_in = self . radio_messages_every
except ( KeyError , ValueError ) :
logger . warning ( " Radio messages config error, disabling them. " )
self . radio_messages = [ ]
self . radio_messages_every = math . inf
self . radio_messages_next_in = math . inf
else :
logger . info ( " Radio messages are force-disabled. " )
self . radio_messages = [ ]
self . radio_messages_every = math . inf
self . radio_messages_next_in = math . inf
2018-12-03 23:56:29 +00:00
except ( KeyError , ValueError ) :
logger . warning ( " Radio messages config error, disabling them. " )
2019-01-02 19:33:15 +00:00
self . radio_messages = [ ]
2018-12-03 23:56:29 +00:00
self . radio_messages_every = math . inf
self . radio_messages_next_in = math . inf
# Activity reporting
try :
self . activity_report_sample_time = int ( config [ " Discord " ] [ " activityreport_sample_time " ] )
except ( KeyError , ValueError ) :
logger . warning ( " Activity reporting config error, disabling it. " )
self . activity_report_sample_time = math . inf
2019-01-02 19:21:06 +00:00
# Sentry error reporting
try :
self . sentry_token = config [ " Sentry " ] [ " token " ]
except ( KeyError , ValueError ) :
logger . warning ( " Sentry client config error, disabling it. " )
self . sentry_token = None
2019-01-02 19:44:10 +00:00
# Easter eggs
try :
if config [ " Discord " ] [ " song_text_easter_eggs_enabled " ] == " True " :
self . song_text_easter_eggs = {
" despacito " : " :arrow_forward: this is so sad. alexa play {song} " ,
" faded " : " :arrow_forward: Basta Garf, lasciami ascoltare {song} " ,
" ligma " : " :arrow_forward: What is ligma? {song} ! " ,
" sugma " : " :arrow_forward: What is sugma? {song} ! " ,
" sugondese " : " :arrow_forward: What is sugondese? {song} ! " ,
" bofa " : " :arrow_forward: What is bofa? {song} ! " ,
" updog " : " :arrow_forward: What ' s up, dog? {song} ! " ,
" sayo-nara " : " :arrow_forward: I gently open the door. {song} awaits me inside. " ,
" monika " : " :arrow_forward: Just Monika. Just Monika. Just {song} . " ,
" country road " : " :arrow_forward: Take me home, to {song} , the place I belong! " ,
" never gonna give you up " : " :arrow_forward: Rickrolling in 2018. Enjoy {song} ! " ,
" september " : " :arrow_forward: Do you remember? {song} . " ,
" homestuck " : " :arrow_forward: > Enter song name. {song} " ,
" undertale " : " :arrow_forward: Howdy! I ' m Flowey! Listen to this friendly song: {song} " ,
" pumped up kicks " : " :arrow_forward: Non metterti mica in testa strane idee ascoltando {song} ... " ,
" jesus " : " :arrow_forward: Respawn in 3 giorni. Intanto, ascolta {song} . " ,
" through The fire And flames " : " :arrow_forward: Fai {song} su osu!, se ne sei capace! " ,
" slow clap " : " :arrow_forward: :clap: :clap: :clap: {song} :clap: :clap: :clap: " ,
" pub scrubs " : " :arrow_forward: MAV COME BACK WE MISS {song} ! " ,
" alleluia " : " :arrow_forward: Wah. Waaaah. Waluigi tiime: {song} " ,
" wah " : " :arrow_forward: Wah. Waaaah. Waluigi tiime: {song} " ,
" waluigi " : " :arrow_forward: Wah. Waaaah. Waluigi tiime: {song} " ,
" nyan cat " : " :arrow_forward: Meow! :3 {song} " ,
" dragonborn " : " :arrow_forward: FUS RO {song} ! " ,
" dovahkiin " : " :arrow_forward: FUS RO {song} ! " ,
" initial d " : " :arrow_forward: Guarda mamma sto driftando sul balcone di Balu grazie a {song} ! " ,
" persona " : " :arrow_forward: You ' ll never see {song} comiiiiing! " ,
" flamingo " : " :arrow_forward: How many {song} do you have to eat? " ,
" linkin park " : " :arrow_forward: Crawling in my {song} ! " ,
" magicite " : " ⚠️ Warning: {song} contiene numerosi bug. E ' ora in riproduzione. " ,
" papers please " : " :arrow_forward: Glory to Arstotzka! {song} ! " ,
" we are number one " : " :arrow_forward: Now paying respect to Robbie Rotten: {song} " ,
" jump up superstar " : " :arrow_forward: Is {song} the Tengen Toppa Guren Lagann opening? " ,
" the world revolving " : " :arrow_forward: CHAOS! CHAOS! I CAN DO {song} ! " ,
" deltarune " : " :arrow_forward: You hug Ralsei. A strange music starts playing: {song} " ,
" song of unhealing " : " :arrow_forward: BEN {song} " ,
" police academy " : " :arrow_forward: {song} - freedom.png " ,
" super smash bros. ultimate " : " :arrow_forward: Re-awaken the undying light with {song} ! " ,
" powerwolf " : " :arrow_forward: Spaggia, ma non ti sei un po ' stancato di {song} ? " ,
" eurobeat " : " :arrow_forward: Nemesis approva la scelta di {song} . Ben fatto, amico. " ,
" k/da " : " :arrow_forward: Che noia... \n "
" Non ci si può nemmeno divertire con {song} che c ' è qualcuno che se ne lamenta. \n "
" La prossima volta, metti qualcosa di diverso, per piacere. " ,
2019-01-02 19:51:43 +00:00
" youtube rewind " : " :arrow_forward: Perchè ti vuoi così male? "
" Sigh, ascolta, discutere con te è inutile. "
2019-01-02 19:44:10 +00:00
" Ti lascio qui {song} . Richiamami quando sarà tutto finito. "
}
else :
self . song_text_easter_eggs = { }
except ( KeyError , ValueError ) :
logger . warning ( " Song text easter egg information not found, defaulting to disabled. " )
self . song_text_easter_eggs = { }
2019-01-04 17:44:03 +00:00
# Discord command prefix
try :
self . command_prefix = config [ " Discord " ] [ " command_prefix " ]
except ( KeyError , ValueError ) :
logger . warning ( " Command prefix not set, defaulting to ' ! ' . " )
self . command_prefix = " ! "
2019-01-04 18:33:00 +00:00
# Youtube API Key
try :
self . youtube_api_key = config [ " YouTube " ] [ " youtube_data_api_key " ]
except ( KeyError , ValueError ) :
logger . warning ( " Youtube API key not set, disabling suggestion mode. " )
self . youtube_api_key = None
2018-11-28 14:47:08 +00:00
2018-12-03 23:56:29 +00:00
# noinspection PyAsyncCall
2018-08-07 23:30:43 +00:00
async def on_ready ( self ) :
# Get the main guild
2018-11-28 14:47:08 +00:00
self . main_guild = self . get_guild ( self . main_guild_id )
2018-08-07 23:30:43 +00:00
if not isinstance ( self . main_guild , discord . Guild ) :
2018-11-26 14:46:47 +00:00
raise errors . InvalidConfigError ( " The main guild does not exist! " )
2018-11-28 14:47:08 +00:00
# Get the main channel
self . main_channel = self . get_channel ( self . main_channel_id )
if not isinstance ( self . main_channel , discord . TextChannel ) :
raise errors . InvalidConfigError ( " The main channel is not a TextChannel! " )
# Show yourself!
2018-08-07 23:30:43 +00:00
await self . change_presence ( status = discord . Status . online , activity = None )
2018-09-18 23:04:48 +00:00
logger . info ( " Bot is ready! " )
2018-11-28 14:47:08 +00:00
# Start the bot tasks
2018-11-18 16:45:14 +00:00
asyncio . ensure_future ( self . activity_task ( ) )
2018-08-07 23:30:43 +00:00
async def on_message ( self , message : discord . Message ) :
if message . channel != self . main_channel or message . author . bot :
return
2019-01-02 19:21:06 +00:00
self . sentry . user_context ( {
2018-08-07 23:30:43 +00:00
" discord " : {
" discord_id " : message . author . id ,
" name " : message . author . name ,
" discriminator " : message . author . discriminator
}
} )
2019-01-04 17:58:36 +00:00
if not message . content . startswith ( self . command_prefix ) :
2018-12-04 10:18:00 +00:00
await message . channel . send ( f " ⚠️ In questa chat sono consentiti solo comandi per il bot. \n "
2018-08-07 23:30:43 +00:00
f " Riinvia il tuo messaggio in un altro canale! " )
await message . delete ( )
return
data = message . content . split ( " " )
2018-08-08 12:09:57 +00:00
if data [ 0 ] not in self . commands :
2018-12-04 10:18:00 +00:00
await message . channel . send ( " ⚠️ Comando non riconosciuto. " )
2018-08-07 23:30:43 +00:00
return
2018-09-18 23:04:48 +00:00
logger . debug ( f " Received command: { message . content } " )
2019-01-02 19:21:06 +00:00
self . sentry . extra_context ( {
2018-09-05 17:48:34 +00:00
" command " : data [ 0 ] ,
" message " : message
} )
2018-09-14 23:05:19 +00:00
self . inactivity_timer = 3600
2018-08-08 12:09:57 +00:00
await self . commands [ data [ 0 ] ] ( channel = message . channel ,
author = message . author ,
params = data )
2017-11-06 15:54:36 +00:00
2018-08-07 23:30:43 +00:00
async def on_error ( self , event_method , * args , * * kwargs ) :
ei = sys . exc_info ( )
2018-12-03 23:56:29 +00:00
logger . critical ( f " Critical error: { repr ( ei ) } " )
2018-09-13 16:38:21 +00:00
# noinspection PyBroadException
2018-08-07 23:30:43 +00:00
try :
2018-09-14 23:05:19 +00:00
await self . main_channel . send ( f " ☢️ **ERRORE CRITICO NELL ' EVENTO** ` { event_method } `! \n "
2018-08-07 23:30:43 +00:00
f " Il bot si è chiuso e si dovrebbe riavviare entro qualche minuto. \n "
f " Una segnalazione di errore è stata automaticamente mandata a Steffo. \n \n "
f " Dettagli dell ' errore: \n "
f " ```python \n "
2018-12-03 23:56:29 +00:00
f " { repr ( ei ) } \n "
2018-08-07 23:30:43 +00:00
f " ``` " )
await self . change_presence ( status = discord . Status . invisible )
await self . close ( )
2018-09-13 16:38:21 +00:00
except Exception :
logger . error ( f " Double critical error: { sys . exc_info ( ) } " )
2018-08-07 23:30:43 +00:00
loop . stop ( )
2019-01-02 19:21:06 +00:00
self . sentry . captureException ( exc_info = ei )
2018-08-07 23:30:43 +00:00
exit ( 1 )
async def feed_pipe ( self , connection ) :
await self . wait_until_ready ( )
while True :
msg = await loop . run_in_executor ( executor , connection . recv )
2018-09-18 23:04:48 +00:00
logger . debug ( f " Received from the Telegram-Discord pipe: { msg } " )
2018-12-29 00:26:30 +00:00
full_cv = ( msg == " get cv full " )
if msg . startswith ( " get cv " ) :
2018-08-07 23:30:43 +00:00
discord_members = list ( self . main_guild . members )
2018-08-28 13:48:12 +00:00
channels = { 0 : None }
members_in_channels = { 0 : [ ] }
message = " "
# Find all the channels
for member in discord_members :
2018-08-28 13:49:29 +00:00
if member . voice is not None :
2018-08-28 13:48:12 +00:00
channel = members_in_channels . get ( member . voice . channel . id )
if channel is None :
members_in_channels [ member . voice . channel . id ] = list ( )
channel = members_in_channels [ member . voice . channel . id ]
channels [ member . voice . channel . id ] = member . voice . channel
channel . append ( member )
else :
members_in_channels [ 0 ] . append ( member )
# Edit the message, sorted by channel
2018-09-18 23:04:48 +00:00
for channel in sorted ( channels , key = lambda c : - c ) :
2018-08-28 13:48:12 +00:00
members_in_channels [ channel ] . sort ( key = lambda x : x . nick if x . nick is not None else x . name )
if channel == 0 :
2018-09-18 23:04:48 +00:00
message + = " <b>Non in chat vocale:</b> \n "
2018-08-28 13:48:12 +00:00
else :
2018-11-07 19:50:55 +00:00
message + = f " <b>In # { escape ( channels [ channel ] . name ) } :</b> \n "
2018-08-28 13:48:12 +00:00
for member in members_in_channels [ channel ] :
2018-09-18 23:04:48 +00:00
# Ignore not-connected non-notable members
2018-12-29 00:26:30 +00:00
if not full_cv and channel == 0 and len ( member . roles ) < 2 :
2018-09-18 23:04:48 +00:00
continue
# Ignore offline members
2018-08-28 13:51:06 +00:00
if member . status == discord . Status . offline and member . voice is None :
2018-08-28 13:48:12 +00:00
continue
# Online status emoji
if member . bot :
message + = " 🤖 "
elif member . status == discord . Status . online :
message + = " 🔵 "
elif member . status == discord . Status . idle :
message + = " ⚫️ "
elif member . status == discord . Status . dnd :
message + = " 🔴 "
elif member . status == discord . Status . offline :
message + = " ⚪️ "
# Voice
if channel != 0 :
# Voice status
if member . voice . self_deaf :
message + = f " 🔇 "
elif member . voice . self_mute :
message + = f " 🔈 "
else :
message + = f " 🔊 "
# Nickname
if member . nick is not None :
2018-11-07 19:50:55 +00:00
message + = escape ( member . nick )
2018-08-28 13:48:12 +00:00
else :
2018-11-07 19:50:55 +00:00
message + = escape ( member . name )
2018-08-28 13:48:12 +00:00
# Game or stream
if member . activity is not None :
2018-09-18 23:04:48 +00:00
if member . activity . type == discord . ActivityType . playing :
2018-11-07 19:50:55 +00:00
message + = f " | 🎮 { escape ( member . activity . name ) } "
2018-09-18 23:04:48 +00:00
# Rich presence
2018-09-19 16:20:56 +00:00
try :
2018-11-06 22:11:35 +00:00
if member . activity . state is not None :
2019-01-02 19:51:43 +00:00
message + = f " ( { escape ( member . activity . state ) } " \
f " | { escape ( member . activity . details ) } ) "
2018-09-19 16:20:56 +00:00
except AttributeError :
2018-12-29 00:50:37 +00:00
pass
2018-09-18 23:04:48 +00:00
elif member . activity . type == discord . ActivityType . streaming :
2018-11-07 19:50:55 +00:00
message + = f " | 📡 [ { escape ( member . activity . name ) } ]( { escape ( member . activity . url ) } ) "
2018-09-18 23:04:48 +00:00
elif member . activity . type == discord . ActivityType . listening :
2018-11-07 19:50:55 +00:00
message + = f " | 🎧 { escape ( member . activity . name ) } "
2018-08-28 13:48:12 +00:00
elif member . activity . type == discord . ActivityType . watching :
2018-11-07 19:50:55 +00:00
message + = f " | 📺 { escape ( member . activity . name ) } "
2018-08-28 13:48:12 +00:00
message + = " \n "
message + = " \n "
connection . send ( message )
2018-09-18 23:04:48 +00:00
logger . debug ( f " Answered successfully cvlist request. " )
2018-08-07 23:30:43 +00:00
elif msg . startswith ( " ! " ) :
data = msg . split ( " " )
2018-08-08 12:09:57 +00:00
if data [ 0 ] not in self . commands :
2018-08-07 23:30:43 +00:00
connection . send ( " error " )
continue
2018-09-18 23:04:48 +00:00
logger . debug ( f " Received command: { msg } " )
2018-08-08 12:09:57 +00:00
await self . main_channel . send ( f " { msg } \n "
f " _(da Telegram)_ " )
2018-09-05 17:48:34 +00:00
await self . commands [ data [ 0 ] ] ( channel = self . main_channel ,
2018-08-08 12:09:57 +00:00
author = None ,
params = data )
2018-08-07 23:30:43 +00:00
connection . send ( " success " )
2018-02-25 18:50:57 +00:00
2018-11-18 16:38:02 +00:00
async def create_activityreport ( self ) :
logger . debug ( " Fetching Discord users... " )
discord_users = list ( self . main_guild . members )
online_members_count = 0
ingame_members_count = 0
cv_count = 0
cv_members_count = 0
non_empty_channels = [ ]
for member in discord_users :
if member . bot :
continue
2018-11-19 23:07:53 +00:00
if member . voice is not None and member . voice . channel != self . main_guild . afk_channel :
2018-11-18 16:38:02 +00:00
cv_count + = 1
if member . voice . channel . id not in non_empty_channels :
non_empty_channels . append ( member . voice . channel . id )
if len ( member . roles ) > = 2 :
2018-11-19 23:07:53 +00:00
if member . voice is not None and member . voice . channel != self . main_guild . afk_channel :
2018-11-18 16:38:02 +00:00
cv_members_count + = 1
if member . status != discord . Status . offline and member . status != discord . Status . idle :
online_members_count + = 1
if member . activity is not None and member . activity . type == discord . ActivityType . playing :
ingame_members_count + = 1
logger . debug ( " Creating and committing db.ActivityReport... " )
session = db . Session ( )
activityreport = db . ActivityReport ( timestamp = datetime . datetime . now ( ) ,
discord_members_online = online_members_count ,
discord_members_ingame = ingame_members_count ,
discord_cv = cv_count ,
discord_members_cv = cv_members_count ,
discord_channels_used = len ( non_empty_channels ) )
session . add ( activityreport )
await loop . run_in_executor ( executor , session . commit )
await loop . run_in_executor ( executor , session . close )
logger . info ( " ActivityReport created. " )
async def activity_task ( self ) :
2018-11-18 16:42:49 +00:00
await self . wait_until_ready ( )
2018-12-03 23:56:29 +00:00
if self . activity_report_sample_time == math . inf :
return
2018-11-18 16:38:02 +00:00
while True :
await self . create_activityreport ( )
2018-12-03 23:56:29 +00:00
logger . debug ( f " Waiting { self . activity_report_sample_time } seconds before the next record. " )
await asyncio . sleep ( self . activity_report_sample_time )
2018-11-18 16:38:02 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def null ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
pass
2018-05-25 17:58:30 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def cmd_ping ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
await channel . send ( f " Pong! " )
2018-05-25 17:58:30 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def cmd_register ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2018-07-31 21:41:05 +00:00
session = db . Session ( )
2018-08-08 12:09:57 +00:00
if len ( params ) < 1 :
await channel . send ( " ⚠️ Non hai specificato un username! \n "
" Sintassi corretta: `!register <username_ryg>` " )
2018-05-26 08:50:54 +00:00
return
2018-08-08 12:09:57 +00:00
try :
# noinspection PyTypeChecker
d = db . Discord . create ( session ,
royal_username = params [ 0 ] ,
discord_user = author )
except errors . AlreadyExistingError :
2018-12-04 10:18:00 +00:00
await channel . send ( " ⚠️ Il tuo account Discord è già collegato a un account RYG "
2018-08-08 12:09:57 +00:00
" o l ' account RYG che hai specificato è già collegato a un account Discord. " )
return
session . add ( d )
session . commit ( )
session . close ( )
await channel . send ( " ✅ Sincronizzazione completata! " )
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def cmd_cv ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
@requires_connected_voice_client
async def cmd_play ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
@requires_connected_voice_client
async def cmd_skip ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-01 23:11:43 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
@requires_connected_voice_client
async def cmd_remove ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def cmd_queue ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
@requires_connected_voice_client
async def cmd_shuffle ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
@requires_connected_voice_client
async def cmd_clear ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-08 12:09:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-08 12:09:57 +00:00
@command
async def cmd_radiomessages ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-06-13 22:10:57 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-16 17:33:38 +00:00
@command
@requires_connected_voice_client
async def cmd_pause ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-17 13:09:03 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-08-17 13:09:03 +00:00
@command
@requires_connected_voice_client
async def cmd_resume ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-08-16 17:33:38 +00:00
2019-01-02 19:51:43 +00:00
# noinspection PyUnusedLocal
2018-12-04 10:18:00 +00:00
@command
@requires_connected_voice_client
2018-12-06 23:31:12 +00:00
async def cmd_mode ( self , channel : discord . TextChannel , author : discord . Member , params : typing . List [ str ] ) :
2019-04-11 16:09:18 +00:00
await channel . send ( " ⚠️ discord.py è stato aggiornato e non è più compatibile con questa versione del bot. La musica non sarà disponibile fino alla release di **Royalnet Unity**... " )
2018-12-04 10:18:00 +00:00
2018-08-16 17:33:38 +00:00
2018-01-15 12:42:40 +00:00
def process ( users_connection = None ) :
2018-08-16 17:46:07 +00:00
logger . info ( " Initializing the bot... " )
2018-08-07 23:30:43 +00:00
bot = RoyalDiscordBot ( )
2018-01-15 12:42:40 +00:00
if users_connection is not None :
2018-08-16 17:46:07 +00:00
logger . info ( " Initializing Telegram-Discord connection... " )
2018-08-07 23:30:43 +00:00
asyncio . ensure_future ( bot . feed_pipe ( users_connection ) )
2018-08-16 17:46:07 +00:00
logger . info ( " Logging in... " )
2018-11-28 14:47:08 +00:00
loop . run_until_complete ( bot . login ( bot . token , bot = True ) )
2018-08-16 17:46:07 +00:00
logger . info ( " Connecting... " )
2018-09-17 22:07:00 +00:00
loop . run_until_complete ( bot . connect ( ) )
logger . info ( " Now stopping... " )
2018-12-08 11:31:26 +00:00
loop . run_until_complete ( bot . main_channel . send ( " ℹ ️ Spegnimento bot in corso..." ) )
2018-09-17 22:07:00 +00:00
loop . run_until_complete ( bot . logout ( ) )
2018-01-15 12:42:40 +00:00
if __name__ == " __main__ " :
2018-01-25 14:30:07 +00:00
process ( )