2017-11-01 18:23:11 +00:00
import datetime
2017-10-27 11:38:32 +00:00
import discord
2017-10-31 22:50:05 +00:00
import discord . opus
2017-11-01 18:23:11 +00:00
import functools
2017-10-27 11:38:32 +00:00
import db
import errors
2017-11-05 21:50:37 +00:00
import youtube_dl
2017-11-07 17:44:00 +00:00
import sqlalchemy . exc
2017-10-27 11:38:32 +00:00
# Init the event loop
import asyncio
loop = asyncio . get_event_loop ( )
# Init the config reader
import configparser
config = configparser . ConfigParser ( )
config . read ( " config.ini " )
# Init the discord bot
client = discord . Client ( )
2017-10-31 22:50:05 +00:00
discord . opus . load_opus ( " libopus-0.dll " )
voice_client = None
voice_player = None
2017-11-07 17:44:00 +00:00
voice_queue = [ ]
voice_playing = None
2017-10-27 11:38:32 +00:00
2017-11-01 18:23:11 +00:00
2017-11-06 15:54:36 +00:00
class Video :
def __init__ ( self ) :
self . user = None
self . info = None
2017-11-07 17:44:00 +00:00
self . enqueued = None
self . channel = None
2017-11-01 18:23:11 +00:00
2017-11-06 15:54:36 +00:00
@staticmethod
2017-11-07 17:44:00 +00:00
async def init ( author , info , enqueued , channel ) :
2017-11-06 15:54:36 +00:00
self = Video ( )
discord_user = await find_user ( author )
2017-11-07 17:44:00 +00:00
self . user = discord_user . royal if discord_user is not None else None
2017-11-06 15:54:36 +00:00
self . info = info
2017-11-07 17:44:00 +00:00
self . enqueued = enqueued
self . channel = channel
2017-11-06 15:54:36 +00:00
return self
2017-11-01 18:23:11 +00:00
2017-11-07 17:44:00 +00:00
def add_to_db ( self , started ) :
db . CVMusic . create_and_add ( url = self . info [ " webpage_url " ] ,
2017-11-06 15:54:36 +00:00
user = self . user ,
2017-11-07 17:44:00 +00:00
enqueued = self . enqueued ,
started = started )
2017-11-06 15:54:36 +00:00
def create_embed ( self ) :
embed = discord . Embed ( type = " rich " ,
title = self . info [ ' title ' ] if ' title ' in self . info else None ,
url = self . info [ ' webpage_url ' ] if ' webpage_url ' in self . info else None ,
colour = discord . Colour ( 13375518 ) )
# Uploader
if " uploader " in self . info and self . info [ " uploader " ] is not None :
embed . set_author ( name = self . info [ " uploader " ] ,
url = self . info [ " uploader_url " ] if " uploader_url " in self . info else None )
# Thumbnail
if " thumbnail " in self . info :
embed . set_thumbnail ( url = self . info [ " thumbnail " ] )
# Duration
embed . add_field ( name = " Durata " , value = str ( datetime . timedelta ( seconds = self . info [ " duration " ] ) ) )
# Views
if " view_count " in self . info and self . info [ " view_count " ] is not None :
embed . add_field ( name = " Visualizzazioni " , value = " { :_} " . format ( self . info [ " view_count " ] ) . replace ( " _ " , " " ) )
# Likes
if " like_count " in self . info and self . info [ " like_count " ] is not None :
embed . add_field ( name = " Mi piace " , value = " { :_} " . format ( self . info [ " like_count " ] ) . replace ( " _ " , " " ) )
# Dislikes
if " dislike_count " in self . info and self . info [ " dislike_count " ] is not None :
embed . add_field ( name = " Non mi piace " , value = " { :_} " . format ( self . info [ " dislike_count " ] ) . replace ( " _ " , " " ) )
return embed
2017-11-05 21:50:37 +00:00
2017-11-06 15:54:36 +00:00
async def download ( self ) :
2017-11-07 17:44:00 +00:00
try :
with youtube_dl . YoutubeDL ( { " noplaylist " : True ,
" format " : " bestaudio " ,
" postprocessors " : [ {
" key " : ' FFmpegExtractAudio ' ,
" preferredcodec " : ' opus '
} ] ,
" outtmpl " : " music. %(ext)s " } ) as ytdl :
info = await loop . run_in_executor ( None , functools . partial ( ytdl . extract_info , self . info [ " webpage_url " ] ) )
except Exception as e :
client . send_message ( self . channel , f " ⚠ Errore durante il download del video: \n "
f " ``` "
f " { e } "
f " ``` " , embed = self . create_embed ( ) )
2017-11-06 15:54:36 +00:00
async def find_user ( user : discord . User ) :
session = await loop . run_in_executor ( None , db . Session )
user = await loop . run_in_executor ( None , session . query ( db . Discord ) . filter_by ( discord_id = user . id ) . join ( db . Royal ) . first )
return user
2017-11-05 21:50:37 +00:00
2017-10-27 11:38:32 +00:00
@client.event
async def on_message ( message : discord . Message ) :
2017-11-07 17:44:00 +00:00
global voice_queue
global voice_player
2017-11-01 18:23:11 +00:00
if message . content . startswith ( " !register " ) :
await client . send_typing ( message . channel )
session = await loop . run_in_executor ( None , db . Session ( ) )
try :
username = message . content . split ( " " , 1 ) [ 1 ]
except IndexError :
2017-11-05 21:50:37 +00:00
await client . send_message ( message . channel , " ⚠️ Non hai specificato un username! \n "
" Sintassi corretta: `!register <username_ryg>` " )
2017-11-01 18:23:11 +00:00
return
try :
d = db . Discord . create ( session ,
royal_username = username ,
discord_user = message . author )
except errors . AlreadyExistingError :
await client . send_message ( message . channel , " ⚠ Il tuo account Discord è già collegato a un account RYG o l ' account RYG che hai specificato è già collegato a un account Discord. " )
return
session . add ( d )
session . commit ( )
2017-10-30 12:45:38 +00:00
session . close ( )
2017-11-01 18:23:11 +00:00
await client . send_message ( message . channel , " ✅ Sincronizzazione completata! " )
elif message . content . startswith ( " !cv " ) and discord . opus . is_loaded ( ) :
await client . send_typing ( message . channel )
if message . author . voice . voice_channel is None :
await client . send_message ( message . channel , " ⚠ Non sei in nessun canale! " )
return
global voice_client
2017-11-07 17:44:00 +00:00
if voice_client is not None and voice_client . is_connected ( ) :
await voice_client . move_to ( message . author . voice . voice_channel )
else :
voice_client = await client . join_voice_channel ( message . author . voice . voice_channel )
2017-11-01 18:23:11 +00:00
await client . send_message ( message . channel , f " ✅ Mi sono connesso in <# { message . author . voice . voice_channel . id } >. " )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !madd " ) :
2017-11-01 18:23:11 +00:00
await client . send_typing ( message . channel )
2017-11-05 21:50:37 +00:00
# The bot should be in voice chat
if voice_client is None :
await client . send_message ( message . channel , " ⚠️ Non sono connesso alla cv! \n "
" Fammi entrare scrivendo `!cv` mentre sei in chat vocale. " )
# Find the sent url
2017-11-01 18:23:11 +00:00
try :
url = message . content . split ( " " , 1 ) [ 1 ]
except IndexError :
2017-11-05 21:50:37 +00:00
await client . send_message ( message . channel , " ⚠️ Non hai specificato un url! \n "
" Sintassi corretta: `!madd <video>` " )
2017-11-01 18:23:11 +00:00
return
2017-11-05 21:50:37 +00:00
# Se è una playlist, informa che potrebbe essere richiesto un po' di tempo
if " playlist " in url :
await client . send_message ( message . channel , f " ℹ ️ Hai inviato una playlist al bot.\n "
f " L ' elaborazione potrebbe richiedere un po ' di tempo. " )
# Extract the info from the url
try :
with youtube_dl . YoutubeDL ( { " quiet " : True , " skip_download " : True , " noplaylist " : True , " format " : " webm[abr>0]/bestaudio/best " } ) as ytdl :
info = await loop . run_in_executor ( None , functools . partial ( ytdl . extract_info , url ) )
except youtube_dl . utils . DownloadError as e :
2017-11-07 17:44:00 +00:00
if " is not a valid URL " in str ( e ) or " Unsupported URL " in str ( e ) :
2017-11-05 21:50:37 +00:00
await client . send_message ( message . channel , f " ⚠️ Il link inserito non è valido. \n "
f " Se vuoi cercare un video su YouTube, usa `!msearch <query>` " )
2017-11-01 18:23:11 +00:00
return
2017-11-05 21:50:37 +00:00
if " _type " not in info :
# If target is a single video
2017-11-07 17:44:00 +00:00
video = await Video . init ( author = message . author , info = info , timestamp = datetime . datetime . now ( ) , channel = message . channel )
2017-11-06 15:54:36 +00:00
await client . send_message ( message . channel , f " ✅ Aggiunto alla coda: " , embed = video . create_embed ( ) )
2017-11-07 17:44:00 +00:00
voice_queue . append ( video )
2017-11-05 21:50:37 +00:00
elif info [ " _type " ] == " playlist " :
# If target is a playlist
if len ( info [ " entries " ] ) < 20 :
2017-11-06 15:54:36 +00:00
for single_info in info [ " entries " ] :
2017-11-07 17:44:00 +00:00
video = await Video . init ( author = message . author , info = single_info , timestamp = datetime . datetime . now ( ) , channel = message . channel )
2017-11-06 15:54:36 +00:00
await client . send_message ( message . channel , f " ✅ Aggiunto alla coda: " , embed = video . create_embed ( ) )
2017-11-07 17:44:00 +00:00
voice_queue . append ( video )
2017-11-05 21:50:37 +00:00
else :
await client . send_message ( message . channel , f " ℹ La playlist contiene { len ( info [ ' entries ' ] ) } video. \n "
f " Sei sicuro di volerli aggiungere alla coda? \n "
f " Rispondi **sì** o **no**. \n "
f " _(Il bot potrebbe crashare.)_ " )
answer = await client . wait_for_message ( timeout = 60 , author = message . author , channel = message . channel )
if " sì " in answer . content . lower ( ) or " si " in answer . content . lower ( ) :
2017-11-06 15:54:36 +00:00
for single_info in info [ " entries " ] :
video = await Video . init ( author = message . author , info = single_info ,
2017-11-07 17:44:00 +00:00
timestamp = datetime . datetime . now ( ) , channel = message . channel )
2017-11-06 15:54:36 +00:00
await client . send_message ( message . channel , f " ✅ Aggiunto alla coda: " , embed = video . create_embed ( ) )
2017-11-07 17:44:00 +00:00
voice_queue . append ( video )
2017-11-05 21:50:37 +00:00
elif " no " in answer . content . lower ( ) :
await client . send_message ( message . channel , f " ℹ Operazione annullata." )
return
elif message . content . startswith ( " !msearch " ) :
2017-11-07 17:44:00 +00:00
await client . send_typing ( message . channel )
# The bot should be in voice chat
if voice_client is None :
await client . send_message ( message . channel , " ⚠️ Non sono connesso alla cv! \n "
" Fammi entrare scrivendo `!cv` mentre sei in chat vocale. " )
# Find the sent text
try :
text = message . content . split ( " " , 1 ) [ 1 ]
except IndexError :
await client . send_message ( message . channel , " ⚠️ Non hai specificato il titolo! \n "
" Sintassi corretta: `!msearch <titolo>` " )
return
# Extract the info from the url
try :
with youtube_dl . YoutubeDL ( { " quiet " : True , " skip_download " : True , " noplaylist " : True , " format " : " webm[abr>0]/bestaudio/best " } ) as ytdl :
info = await loop . run_in_executor ( None , functools . partial ( ytdl . extract_info , f " ytsearch: { text } " ) )
except youtube_dl . utils . DownloadError as e :
if " is not a valid URL " in str ( e ) or " Unsupported URL " in str ( e ) :
await client . send_message ( message . channel , f " ⚠️ Il video ottenuto dalla ricerca non è valido. Prova a cercare qualcos ' altro... " )
return
# If target is a single video
video = await Video . init ( author = message . author , info = info [ " entries " ] [ 0 ] , enqueued = datetime . datetime . now ( ) , channel = message . channel )
await client . send_message ( message . channel , f " ✅ Aggiunto alla coda: " , embed = video . create_embed ( ) )
voice_queue . append ( video )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !mskip " ) :
2017-11-07 17:44:00 +00:00
global voice_player
voice_player . stop ( )
voice_player = None
await client . send_message ( message . channel , f " ⏩ Video saltato. " )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !mpause " ) :
2017-11-07 17:44:00 +00:00
if voice_player is None or not voice_player . is_playing ( ) :
await client . send_message ( message . channel , f " ⚠️ Non è in corso la riproduzione di un video, pertanto non c ' è niente da pausare. " )
return
voice_player . pause ( )
await client . send_message ( message . channel , f " ⏸ Riproduzione messa in pausa. \n "
f " Riprendi con `!mresume`. " )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !mresume " ) :
2017-11-07 17:44:00 +00:00
if voice_player is None or voice_player . is_playing ( ) :
await client . send_message ( message . channel , f " ⚠️ Non c ' è nulla in pausa da riprendere! " )
return
voice_player . resume ( )
await client . send_message ( message . channel , f " ▶️ Riproduzione ripresa. " )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !mcancel " ) :
2017-11-07 17:44:00 +00:00
try :
video = voice_queue . pop ( )
except IndexError :
await client . send_message ( message . channel , f " ⚠ La playlist è vuota. " )
return
await client . send_message ( message . channel , f " ❌ Rimosso dalla playlist: " , embed = video . create_embed ( ) )
2017-11-05 21:50:37 +00:00
elif message . content . startswith ( " !mstop " ) :
2017-11-07 17:44:00 +00:00
if voice_player is None :
await client . send_message ( message . channel , f " ⚠ Non c ' è nulla da interrompere! " )
return
voice_queue = [ ]
voice_player . stop ( )
voice_player = None
await client . send_message ( message . channel , f " ⏹ Riproduzione interrotta e playlist svuotata. " )
2017-11-05 21:50:37 +00:00
2017-10-30 12:45:38 +00:00
async def update_users_pipe ( users_connection ) :
await client . wait_until_ready ( )
while True :
msg = await loop . run_in_executor ( None , users_connection . recv )
if msg == " /cv " :
discord_members = list ( client . get_server ( config [ " Discord " ] [ " server_id " ] ) . members )
users_connection . send ( discord_members )
2017-11-05 21:50:37 +00:00
async def update_music_queue ( ) :
2017-11-06 15:54:36 +00:00
await client . wait_until_ready ( )
while True :
global voice_player
# Wait until there is nothing playing
2017-11-07 17:44:00 +00:00
if voice_client is not None and voice_player is not None and ( voice_player . is_playing ( ) and not voice_player . is_done ( ) ) :
await asyncio . sleep ( 1 )
continue
if len ( voice_queue ) == 0 :
await asyncio . sleep ( 1 )
continue
2017-11-06 15:54:36 +00:00
# Get the last video in the queue
2017-11-07 17:44:00 +00:00
video = voice_queue . pop ( 0 )
# Notify the chat of the download
await client . send_message ( video . channel , f " ℹ E' iniziato il download di: " , embed = video . create_embed ( ) )
2017-11-06 15:54:36 +00:00
# Download the video
await video . download ( )
# Play the video
voice_player = voice_client . create_ffmpeg_player ( f " music.opus " )
voice_player . start ( )
2017-11-07 17:44:00 +00:00
# Notify the chat of the start
await client . send_message ( video . channel , f " ▶ Ora in riproduzione in <# { voice_client . channel . id } >: " , embed = video . create_embed ( ) )
2017-11-06 15:54:36 +00:00
# Add the video to the db
2017-11-07 17:44:00 +00:00
try :
await loop . run_in_executor ( None , functools . partial ( video . add_to_db , started = datetime . datetime . now ( ) ) )
except sqlalchemy . exc . OperationalError :
await client . send_message ( video . channel , f " ⚠ Ehi, <@77703771181817856>, il database si è rotto. Vallo a sistemare! " )
2017-11-06 15:54:36 +00:00
2017-11-05 21:50:37 +00:00
2017-10-30 12:45:38 +00:00
def process ( users_connection ) :
print ( " Discordbot starting... " )
loop . create_task ( update_users_pipe ( users_connection ) )
2017-11-06 15:54:36 +00:00
loop . create_task ( update_music_queue ( ) )
2017-10-30 12:45:38 +00:00
client . run ( config [ " Discord " ] [ " bot_token " ] )