Compare commits

..

No commits in common. "main" and "0.1" have entirely different histories.
main ... 0.1

6 changed files with 27 additions and 73 deletions

View file

@ -1,4 +1,7 @@
version=0.1
token=OT.. token=OT..
channel_text=123 channel_text=123
channel_voice=456 channel_voice=456
url=http://example.net url=http://example.net
passes=2
bitrate=48000

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
# Internal # Internal
*.env .env

View file

@ -5,9 +5,7 @@ RUN apt-get -yq install ffmpeg build-essential
RUN pip --no-cache-dir install \ RUN pip --no-cache-dir install \
discord.py[voice] \ discord.py[voice] \
pynacl \ pynacl
httplib2 \
urllib3
WORKDIR /app WORKDIR /app

View file

@ -4,13 +4,12 @@
* Live radio re-streaming to voice channels. * Live radio re-streaming to voice channels.
* Automatically turned on/off when someone joins/leaves the bound channel. * Automatically turned on/off when someone joins/leaves the bound channel.
* Shows ICY StreamTitle
* Other cool things. * Other cool things.
## Running using Docker from the repo ## Running using Docker from the repo
1. `sudo apt install docker-compose` 1. `sudo apt install docker-compose`
2. `cp radiobot.env.dist radiobot.env` 2. `cp .env.dist .env`
3. `vim radiobot.env` 3. `vim .env`
4. `docker-compose up -d --build --remove-orphans` 4. `docker-compose up -d --build --remove-orphans`
5. `docker-compose logs --follow --tail 100 --timestamps` 5. `docker-compose logs --follow --tail 100`

View file

@ -6,6 +6,6 @@ services:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
env_file: env_file:
- radiobot.env - .env
command: python3 -u main.py command: python3 -u main.py
restart: always restart: always

82
main.py
View file

@ -1,31 +1,28 @@
import os import os
import sys import discord
import re from discord.ext import commands
import struct
import asyncio import asyncio
from subprocess import Popen from subprocess import Popen
import discord bot_version = os.environ['version']
from discord.ext import commands print('radiobot ' + bot_version + ' starting')
import urllib.request as urllib2
login_token = os.environ['token'] login_token = os.environ['token']
voice_channel_id = os.environ['channel_voice'] voice_channel_id = os.environ['channel_voice']
text_channel_id = os.environ['channel_text'] text_channel_id = os.environ['channel_text']
source = os.environ['url']
# Configure the bot # Configure the bot
description = '''Radiobot''' description = '''Radiobot'''
bot = commands.Bot(command_prefix='!', description=description) bot = commands.Bot(command_prefix='!', description=description)
# Initialize opus
#if not discord.opus.is_loaded():
# discord.opus.load_opus('opus')
# Initialize some global variables # Initialize some global variables
voice_client = None voice_client = None
text_channel = None text_channel = None
isConnected = False isConnected = False
encoding = 'latin1'
bot_version = '0.2'
print('[INFO] radiobot ' + bot_version + ' starting')
@bot.event @bot.event
async def on_ready(): async def on_ready():
@ -38,7 +35,8 @@ async def on_ready():
print("[WARN] No voice channel " + voice_channel_id + " found!") print("[WARN] No voice channel " + voice_channel_id + " found!")
if not debug_channel: if not debug_channel:
print("[WARN] No text channel " + text_channel_id + " found!") print("[WARN] No text channel " + text_channel_id + " found!")
await debug_channel.send('] ready! :satellite_orbital:')
await debug_channel.send('] ready.')
@bot.event @bot.event
async def on_message(message): async def on_message(message):
@ -51,31 +49,8 @@ async def on_message(message):
print('<' + message.author.nick + '> ' + message.content) print('<' + message.author.nick + '> ' + message.content)
if message.content == '!help':
await message.channel.send('] radiobot commands: help, version, song :star:')
if message.content == '!version': if message.content == '!version':
await message.channel.send('] radiobot ' + bot_version + ' - python ' + os.environ['PYTHON_VERSION'] + ' - github.com/deflax/radiobot :purple_heart:') await message.channel.send('] radiobot ' + bot_version + ' - python ' + os.environ['PYTHON_VERSION'])
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
meta = title.decode(encoding, errors='replace')
metasplit = meta.split('*', 1)[0]
await message.channel.send('] ' + metasplit + ' :musical_note:')
@bot.event @bot.event
async def on_voice_state_update(member, before, after): async def on_voice_state_update(member, before, after):
@ -92,49 +67,28 @@ async def on_voice_state_update(member, before, after):
""" """
global isConnected global isConnected
FFMPEG_OPTS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} FFMPEG_OPTS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
member_msg = None
if member.bot: if member.bot:
#print("[INFO] self event detection") #print("[INFO] self event detection")
return return
debug_channel = await bot.fetch_channel(text_channel_id)
voice_channel = await bot.fetch_channel(voice_channel_id) voice_channel = await bot.fetch_channel(voice_channel_id)
debug_channel = await bot.fetch_channel(text_channel_id)
member_ids = len(voice_channel.voice_states.keys()) member_ids = len(voice_channel.voice_states.keys())
if before.channel is None: #await debug_channel.send('] voice #' + voice_channel_id + ' member count: ' + str(member_ids))
prev_chan = "not_found"
else:
prev_chan = str(before.channel.id)
#if prev_chan == str(voice_channel_id):
# member_msg = str(member.nick) + ' is back in the void :cyclone:'
if after.channel is None: if member_ids == 1 and isConnected == False:
next_chan = "not_found"
else:
next_chan = str(after.channel.id)
if next_chan == str(voice_channel_id):
member_msg = str(member.nick) + ' enjoys! :satellite:'
if prev_chan == next_chan:
#print('[INFO] ' + str(member.nick) + ' activity')
pass
else:
if member_msg is not None:
print('[INFO] ' + member_msg)
await debug_channel.send('] ' + member_msg)
if member_ids > 0 and isConnected == False:
isConnected = True isConnected = True
await debug_channel.send('] connecting to #' + voice_channel_id)
voice_client = await voice_channel.connect() voice_client = await voice_channel.connect()
voice_client.play(discord.FFmpegPCMAudio(source, **FFMPEG_OPTS)) player = voice_client.play(discord.FFmpegPCMAudio(os.environ['url'], **FFMPEG_OPTS))
voice_client.source = discord.PCMVolumeTransformer(voice_client.source)
voice_client.source.volume = 0.3
return return
if member_ids == 1 and isConnected == True: if member_ids == 1 and isConnected == True:
isConnected = False isConnected = False
#await debug_channel.send('] sleeping. :satellite_orbital:') await debug_channel.send('] disconnecting from #' + voice_channel_id)
for voice_client in bot.voice_clients: for voice_client in bot.voice_clients:
await voice_client.disconnect() await voice_client.disconnect()
return return