2022-03-23 19:45:22 -04:00
|
|
|
import os
|
2022-03-28 10:52:08 -04:00
|
|
|
import sys
|
|
|
|
import re
|
|
|
|
import struct
|
2022-03-23 21:13:39 -04:00
|
|
|
import asyncio
|
|
|
|
from subprocess import Popen
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-28 10:52:08 -04:00
|
|
|
import discord
|
|
|
|
from discord.ext import commands
|
|
|
|
import urllib.request as urllib2
|
|
|
|
|
2022-03-23 21:13:39 -04:00
|
|
|
login_token = os.environ['token']
|
|
|
|
voice_channel_id = os.environ['channel_voice']
|
2022-03-23 21:22:35 -04:00
|
|
|
text_channel_id = os.environ['channel_text']
|
2022-03-28 10:52:08 -04:00
|
|
|
source = os.environ['url']
|
2022-03-23 19:56:13 -04:00
|
|
|
|
2022-03-23 21:13:39 -04:00
|
|
|
# Configure the bot
|
2022-03-23 21:22:35 -04:00
|
|
|
description = '''Radiobot'''
|
2022-03-23 21:13:39 -04:00
|
|
|
bot = commands.Bot(command_prefix='!', description=description)
|
2022-03-23 19:59:21 -04:00
|
|
|
|
2022-03-23 21:13:39 -04:00
|
|
|
# Initialize some global variables
|
|
|
|
voice_client = None
|
|
|
|
text_channel = None
|
2022-03-27 22:38:03 -04:00
|
|
|
isConnected = False
|
2022-03-28 10:52:08 -04:00
|
|
|
encoding = 'latin1'
|
2022-03-28 11:11:47 -04:00
|
|
|
bot_version = '0.2'
|
2022-03-28 10:52:08 -04:00
|
|
|
|
2022-03-28 11:21:01 -04:00
|
|
|
print('[INFO] radiobot ' + bot_version + ' starting')
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-23 21:13:39 -04:00
|
|
|
@bot.event
|
|
|
|
async def on_ready():
|
2022-03-27 22:38:03 -04:00
|
|
|
print('[INFO] Logged in as ' + bot.user.name + ' #' + str(bot.user.id))
|
2022-03-23 21:13:39 -04:00
|
|
|
|
|
|
|
# get channels
|
2022-03-27 22:38:03 -04:00
|
|
|
voice_channel = await bot.fetch_channel(voice_channel_id)
|
|
|
|
debug_channel = await bot.fetch_channel(text_channel_id)
|
|
|
|
if not voice_channel:
|
|
|
|
print("[WARN] No voice channel " + voice_channel_id + " found!")
|
|
|
|
if not debug_channel:
|
|
|
|
print("[WARN] No text channel " + text_channel_id + " found!")
|
2022-03-29 00:40:18 -04:00
|
|
|
await debug_channel.send('] ready! :satellite_orbital:')
|
2022-03-23 21:13:39 -04:00
|
|
|
|
|
|
|
@bot.event
|
2022-03-23 21:55:30 -04:00
|
|
|
async def on_message(message):
|
2022-03-23 21:13:39 -04:00
|
|
|
# don't respond to ourselves
|
2022-03-23 21:55:30 -04:00
|
|
|
if message.author == bot.user:
|
2022-03-23 21:13:39 -04:00
|
|
|
return
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-23 22:11:56 -04:00
|
|
|
if not str(message.channel.id) == text_channel_id:
|
|
|
|
return
|
|
|
|
|
|
|
|
print('<' + message.author.nick + '> ' + message.content)
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-28 10:52:08 -04:00
|
|
|
if message.content == '!help':
|
2022-03-28 12:31:46 -04:00
|
|
|
await message.channel.send('] radiobot commands: help, version, song :star:')
|
2022-03-28 10:52:08 -04:00
|
|
|
|
2022-03-23 21:55:30 -04:00
|
|
|
if message.content == '!version':
|
2022-03-28 11:12:55 -04:00
|
|
|
await message.channel.send('] radiobot ' + bot_version + ' - python ' + os.environ['PYTHON_VERSION'] + ' - github.com/deflax/radiobot :purple_heart:')
|
2022-03-23 20:13:18 -04:00
|
|
|
|
2022-03-28 10:52:08 -04:00
|
|
|
if message.content == '!song':
|
|
|
|
request = urllib2.Request(source, headers={'Icy-MetaData': 1}) # request metadata
|
|
|
|
response = urllib2.urlopen(request)
|
|
|
|
metaint = int(response.headers['icy-metaint'])
|
|
|
|
for _ in range(10): # # title may be empty initially, try several times
|
|
|
|
response.read(metaint) # skip to metadata
|
|
|
|
metadata_length = struct.unpack('B', response.read(1))[0] * 16 # length byte
|
|
|
|
metadata = response.read(metadata_length).rstrip(b'\0')
|
|
|
|
m = re.search(br"StreamTitle='([^']*)';", metadata)
|
|
|
|
if m:
|
|
|
|
title = m.group(1)
|
|
|
|
if title:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
print('no title found')
|
|
|
|
return
|
2022-03-28 11:08:55 -04:00
|
|
|
meta = title.decode(encoding, errors='replace')
|
|
|
|
metasplit = meta.split('*', 1)[0]
|
2022-03-28 12:21:45 -04:00
|
|
|
await message.channel.send('] ' + metasplit + ' :musical_note:')
|
2022-03-28 10:52:08 -04:00
|
|
|
|
2022-03-23 21:13:39 -04:00
|
|
|
@bot.event
|
|
|
|
async def on_voice_state_update(member, before, after):
|
2022-03-24 11:21:30 -04:00
|
|
|
"""
|
|
|
|
Starts events when a user changes their voice state.
|
|
|
|
Such as connecting, disconnecting and moving between channels.
|
|
|
|
:type member: discord.Member
|
|
|
|
:type before: discord.VoiceState
|
|
|
|
:type after: discord.VoiceState
|
|
|
|
:param member: The member that changed their voice state.
|
|
|
|
:param before: The member as they were before the change.
|
|
|
|
:param after: The member as they are after the change.
|
|
|
|
:return:
|
|
|
|
"""
|
2022-03-27 22:38:03 -04:00
|
|
|
global isConnected
|
2022-03-27 23:46:36 -04:00
|
|
|
FFMPEG_OPTS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
|
2022-03-28 13:02:22 -04:00
|
|
|
member_msg = None
|
2022-03-27 23:23:10 -04:00
|
|
|
|
|
|
|
if member.bot:
|
|
|
|
#print("[INFO] self event detection")
|
|
|
|
return
|
2022-03-27 21:39:37 -04:00
|
|
|
|
2022-03-27 22:38:03 -04:00
|
|
|
debug_channel = await bot.fetch_channel(text_channel_id)
|
2022-03-28 12:09:45 -04:00
|
|
|
voice_channel = await bot.fetch_channel(voice_channel_id)
|
2022-03-27 23:46:36 -04:00
|
|
|
member_ids = len(voice_channel.voice_states.keys())
|
|
|
|
|
2022-03-28 12:09:45 -04:00
|
|
|
if before.channel is None:
|
|
|
|
prev_chan = "not_found"
|
|
|
|
else:
|
|
|
|
prev_chan = str(before.channel.id)
|
2022-03-28 12:55:13 -04:00
|
|
|
#if prev_chan == str(voice_channel_id):
|
|
|
|
# member_msg = str(member.nick) + ' is back in the void :cyclone:'
|
2022-03-28 12:12:38 -04:00
|
|
|
|
2022-03-28 12:09:45 -04:00
|
|
|
if after.channel is None:
|
|
|
|
next_chan = "not_found"
|
|
|
|
else:
|
|
|
|
next_chan = str(after.channel.id)
|
|
|
|
if next_chan == str(voice_channel_id):
|
2022-03-28 12:31:46 -04:00
|
|
|
member_msg = str(member.nick) + ' enjoys! :satellite:'
|
2022-03-28 12:55:13 -04:00
|
|
|
|
|
|
|
if prev_chan == next_chan:
|
2022-03-28 16:56:13 -04:00
|
|
|
#print('[INFO] ' + str(member.nick) + ' activity')
|
|
|
|
pass
|
2022-03-28 12:55:13 -04:00
|
|
|
else:
|
2022-03-28 13:02:22 -04:00
|
|
|
if member_msg is not None:
|
2022-03-28 12:55:13 -04:00
|
|
|
print('[INFO] ' + member_msg)
|
|
|
|
await debug_channel.send('] ' + member_msg)
|
2022-03-27 22:38:03 -04:00
|
|
|
|
2022-03-28 11:15:12 -04:00
|
|
|
if member_ids > 0 and isConnected == False:
|
2022-03-27 23:23:10 -04:00
|
|
|
isConnected = True
|
2022-03-27 23:46:36 -04:00
|
|
|
voice_client = await voice_channel.connect()
|
2022-03-28 13:02:22 -04:00
|
|
|
voice_client.play(discord.FFmpegPCMAudio(source, **FFMPEG_OPTS))
|
|
|
|
voice_client.source = discord.PCMVolumeTransformer(voice_client.source)
|
2022-03-28 13:06:41 -04:00
|
|
|
voice_client.source.volume = 0.3
|
2022-03-27 23:23:10 -04:00
|
|
|
return
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-27 23:23:10 -04:00
|
|
|
if member_ids == 1 and isConnected == True:
|
2022-03-27 22:38:03 -04:00
|
|
|
isConnected = False
|
2022-03-28 13:02:22 -04:00
|
|
|
#await debug_channel.send('] sleeping. :satellite_orbital:')
|
2022-03-27 23:46:36 -04:00
|
|
|
for voice_client in bot.voice_clients:
|
|
|
|
await voice_client.disconnect()
|
2022-03-27 23:23:10 -04:00
|
|
|
return
|
2022-03-23 19:27:00 -04:00
|
|
|
|
2022-03-23 21:55:30 -04:00
|
|
|
# Start the bot with multiprocess compatiblity
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
bot.loop.run_until_complete(bot.start(login_token))
|
|
|
|
finally:
|
|
|
|
bot.loop.close()
|