188 lines
6.6 KiB
Python
188 lines
6.6 KiB
Python
import discord
|
|
from datetime import datetime
|
|
from gtts import gTTS
|
|
from forex_python.converter import CurrencyRates
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
from apscheduler.triggers.cron import CronTrigger
|
|
from random import randint
|
|
import asyncio
|
|
import json
|
|
import time
|
|
import hashlib
|
|
import os.path
|
|
|
|
ROOT = "/home/bot/ticker"
|
|
|
|
with open(ROOT + "/config.json") as file:
|
|
conf = json.load(file)
|
|
|
|
# guilds
|
|
SEGUIS = conf['guilds']['seguis']
|
|
GOONS = conf['guilds']['goons']
|
|
|
|
# users
|
|
MATT = conf['users']['matt']
|
|
DAD = conf['users']['dad']
|
|
CHEW = conf['users']['chew']
|
|
|
|
# channels
|
|
SMOKEYS = conf['channels']['smokeys']
|
|
RETARD = conf['channels']['retard_dimension']
|
|
GHETTO = conf['channels']['league_ghetto']
|
|
|
|
client = discord.Client()
|
|
c = CurrencyRates()
|
|
s = AsyncIOScheduler(event_loop = client.loop)
|
|
|
|
ticker_queue = asyncio.Queue()
|
|
|
|
# returns a file path from the cache
|
|
def to_tts(message, lang = 'ja', cache = True):
|
|
# the file we save to is based on the hash of the message
|
|
file_name = ROOT + "/dqn.mp3"
|
|
if cache:
|
|
message_hash = hashlib.md5(message.encode('utf-8')).hexdigest()
|
|
file_name = ROOT + "/cache/" + message_hash + ".mp3"
|
|
|
|
print(message)
|
|
|
|
# the hash lets us cache
|
|
if not os.path.isfile(file_name) or not cache:
|
|
print("file does not exist, cacheing")
|
|
tts = gTTS(message, lang = lang)
|
|
tts.save(file_name)
|
|
|
|
return file_name
|
|
|
|
# announcer loop
|
|
async def ticker_update():
|
|
while True:
|
|
await client.wait_until_ready()
|
|
|
|
# blocks until there is an event waiting in the queue
|
|
announce_file, target_voice_channel, after = await ticker_queue.get()
|
|
print("ticker event has been removed from queue")
|
|
|
|
# there are three scenarios here
|
|
# 1. we are not connected to a voice channel. we should CONNECT to the target voice channel
|
|
# 2. we are connected to the wrong channel. we should MOVE to the target channel
|
|
# 3. we are already in the correct channel. we should do nothing
|
|
|
|
guild = target_voice_channel.guild
|
|
voice_client = guild.voice_client # may be none
|
|
|
|
# connect vc
|
|
if not (voice_client and voice_client.is_connected()):
|
|
voice_client = await target_voice_channel.connect()
|
|
|
|
# switch channels
|
|
if not target_voice_channel.id == voice_client.channel.id:
|
|
await voice_client.move_to(target_voice_channel)
|
|
|
|
source = discord.FFmpegOpusAudio(announce_file)
|
|
voice_client.play(source)
|
|
|
|
# block while the message is playing
|
|
while voice_client.is_playing():
|
|
await asyncio.sleep(0.1)
|
|
|
|
if after:
|
|
await after(target_voice_channel)
|
|
|
|
# the queue being empty indicates all events completed
|
|
if ticker_queue.empty():
|
|
await voice_client.disconnect()
|
|
|
|
@client.event
|
|
async def on_ready():
|
|
print(client.user.name)
|
|
|
|
# add afk message
|
|
@client.event
|
|
async def on_voice_state_update(member, before, after):
|
|
if member.id == MATT and after.channel and after.channel.id == SMOKEYS:
|
|
await asyncio.sleep(randint(3, 30))
|
|
await member.move_to(client.get_channel(RETARD))
|
|
|
|
if member.id == CHEW and not before.channel:
|
|
await add_event(to_tts("Hey everyone, the pineapple is here!", 'en'), after.channel)
|
|
|
|
# only run on moves
|
|
if before.channel and after.channel:
|
|
# moves to the afk channel
|
|
if member.guild.afk_channel and after.channel.id == member.guild.afk_channel.id:
|
|
file_name = to_tts(member.display_name + "はretard dimensionに移動しました")
|
|
await add_event(file_name, before.channel)
|
|
|
|
# either matts channel, or the most active channel
|
|
def get_best_channel(guild_id):
|
|
guild = client.get_guild(guild_id)
|
|
matt = discord.utils.find(lambda m: m.id == MATT, guild.members)
|
|
|
|
# why the fuck does python still not have safe navigation operators?
|
|
matt_voice = ((matt.voice.channel if matt.voice.channel else None) if matt.voice else None) if matt else None
|
|
top_channel = sorted(guild.voice_channels, key = lambda chan: len(chan.members), reverse = True)[0]
|
|
return matt_voice if matt_voice else top_channel
|
|
|
|
# helper
|
|
async def add_event(file_name, voice_channel, after = None):
|
|
print("ticker event has been added to queue")
|
|
await ticker_queue.put((file_name, voice_channel, after))
|
|
|
|
# decorator for simple scheduled announcements
|
|
def update(guild, trigger):
|
|
def decorator(func):
|
|
async def wrapper():
|
|
voice_channel = get_best_channel(guild)
|
|
await add_event(func(), voice_channel)
|
|
s.add_job(wrapper, trigger)
|
|
return wrapper
|
|
return decorator
|
|
|
|
@update(SEGUIS, CronTrigger(hour = '0-19,21-23'))
|
|
def ticker():
|
|
return to_tts("The current JPY exchange rate is " + str(c.get_rate('USD', 'JPY')) + " yen to a dollar")
|
|
|
|
@update(SEGUIS, CronTrigger(hour = '20'))
|
|
def flatten():
|
|
return to_tts("Matt, it is time for your 4 PM dick flattening")
|
|
|
|
@update(GOONS, CronTrigger(hour = '*', minute = '15'))
|
|
def bussing():
|
|
BUS_START = 1587179700
|
|
since_start = time.time() - BUS_START
|
|
hours_since_start = since_start / 60 / 60
|
|
file_name = to_tts("GIVE IT UP FOR " + str(int(hours_since_start)) + " HOURS SINCE DEPARTURE!! WOO!!", 'en', False)
|
|
return file_name
|
|
|
|
# ban league
|
|
async def league_ghetto():
|
|
await client.wait_until_ready()
|
|
|
|
# short circuiting ands are okay, but this is still hell.
|
|
leaguers = filter(lambda member: member.activity and member.activity.type == discord.ActivityType.playing
|
|
and member.activity.name == 'League of Legends' and member.voice and member.voice.channel
|
|
and member.voice.channel.id != GHETTO, client.get_guild(SEGUIS).members)
|
|
|
|
for member in leaguers:
|
|
# using the id because i don't know the internals of this library and i dont care to check
|
|
# its entirely possible that the same object is used after the move_to and if before_channel
|
|
# referenced that then its in the bone zone.
|
|
before_channel = member.voice.channel.id
|
|
|
|
# why doesn't python have async lambdas yet?
|
|
async def move(_):
|
|
await member.move_to(client.get_channel(GHETTO))
|
|
|
|
file_name = to_tts(member.display_name + ", you have been found in violation of the No League Act of 2020."
|
|
+ " Administrative action will now be taken against your account.", lang = 'de')
|
|
await add_event(file_name, client.get_channel(before_channel), move)
|
|
|
|
client.loop.create_task(ticker_update())
|
|
s.add_job(league_ghetto, CronTrigger(minute = '*/5'))
|
|
s.start()
|
|
|
|
with open(ROOT + "/priv/token") as file:
|
|
token = file.readline()
|
|
client.run(token)
|