Skip to content

Commit

Permalink
Merge pull request #255 from DuckBoss/new-callback-system
Browse files Browse the repository at this point in the history
New callback system
  • Loading branch information
DuckBoss authored Sep 12, 2020
2 parents 6942f9b + ae53027 commit dfb1eed
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 18 deletions.
60 changes: 48 additions & 12 deletions JJMumbleBot/core/bot_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import pymumble_py3 as pymumble
from pymumble_py3.constants import PYMUMBLE_CLBK_USERCREATED, PYMUMBLE_CLBK_CONNECTED, PYMUMBLE_CLBK_SOUNDRECEIVED, \
PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, PYMUMBLE_CLBK_DISCONNECTED, PYMUMBLE_CLBK_CHANNELUPDATED, \
PYMUMBLE_CLBK_CHANNELREMOVED, PYMUMBLE_CLBK_CHANNELCREATED, PYMUMBLE_CLBK_USERREMOVED, PYMUMBLE_CLBK_USERUPDATED
from JJMumbleBot.core.callback_service import CallbackService
from JJMumbleBot.lib.utils.web_utils import RemoteTextMessage
from JJMumbleBot.settings import runtime_settings
from JJMumbleBot.settings import global_settings
Expand Down Expand Up @@ -26,6 +30,7 @@ class BotService:
def __init__(self, serv_ip, serv_port, serv_pass):
# Initialize bot services.
global_settings.bot_service = self
global_settings.clbk_service = CallbackService()
# Initialize user settings.
BotServiceHelper.initialize_settings()
# Initialize logging services.
Expand Down Expand Up @@ -77,7 +82,7 @@ def __init__(self, serv_ip, serv_port, serv_pass):
rprint("######### Initializing Mumble Client #########", origin=L_STARTUP)
# Retrieve mumble client data from configs.
mumble_login_data = BotServiceHelper.retrieve_mumble_data(serv_ip, serv_port, serv_pass)
BotService.initialize_mumble(mumble_login_data)
self.initialize_mumble(mumble_login_data)
log(INFO, "######### Initialized Mumble Client #########", origin=L_STARTUP)
rprint("######### Initialized Mumble Client #########", origin=L_STARTUP)
# Initialize web interface
Expand All @@ -91,13 +96,43 @@ def __init__(self, serv_ip, serv_port, serv_pass):
# Start runtime loop.
BotService.loop()

@staticmethod
def initialize_mumble(md: MumbleData):
def initialize_mumble(self, md: MumbleData):
global_settings.mumble_inst = pymumble.Mumble(md.ip_address, port=md.port, user=md.user_id, reconnect=md.auto_reconnect,
password=md.password, certfile=md.certificate, stereo=md.stereo)
global_settings.mumble_inst.callbacks.set_callback('text_received', BotService.message_received)
global_settings.mumble_inst.callbacks.set_callback('sound_received', BotService.sound_received)
global_settings.mumble_inst.callbacks.set_callback('connected', BotService.on_connected)
# Callback - message_received
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_TEXTMESSAGERECEIVED,
global_settings.clbk_service.message_received)
global_settings.core_callbacks.append_to_callback(PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, self.message_received)
# Callback - sound_received
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_SOUNDRECEIVED,
global_settings.clbk_service.sound_received)
global_settings.core_callbacks.append_to_callback(PYMUMBLE_CLBK_SOUNDRECEIVED, self.sound_received)
# Callback - on_connected
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_CONNECTED,
global_settings.clbk_service.connected)
global_settings.core_callbacks.append_to_callback(PYMUMBLE_CLBK_CONNECTED, self.on_connected)
# Callback - disconnected
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_DISCONNECTED,
global_settings.clbk_service.disconnected)
# Callback - user_created
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_USERCREATED,
global_settings.clbk_service.user_created)
# Callback - user_updated
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_USERUPDATED,
global_settings.clbk_service.user_updated)
# Callback - user_removed
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_USERREMOVED,
global_settings.clbk_service.user_removed)
# Callback - channel_created
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_CHANNELCREATED,
global_settings.clbk_service.channel_created)
# Callback - channel_removed
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_CHANNELREMOVED,
global_settings.clbk_service.channel_removed)
# Callback - channel_updated
global_settings.mumble_inst.callbacks.set_callback(PYMUMBLE_CLBK_CHANNELUPDATED,
global_settings.clbk_service.channel_updated)

global_settings.mumble_inst.set_codec_profile('audio')
global_settings.mumble_inst.set_receive_sound(True)
global_settings.mumble_inst.start()
Expand All @@ -110,8 +145,9 @@ def initialize_mumble(md: MumbleData):
runtime_utils.mute()
runtime_utils.get_channel(global_settings.cfg[C_CONNECTION_SETTINGS][P_CHANNEL_DEF]).move_in()

@staticmethod
def message_received(text, remote_cmd=False):
def message_received(self, message):
text = message[0]
remote_cmd = message[1]
all_commands = runtime_utils.parse_message(text)
if all_commands is None:
return
Expand Down Expand Up @@ -183,12 +219,12 @@ def message_received(text, remote_cmd=False):
def process_command_queue(com):
execute_cmd.execute_command(com)

@staticmethod
def on_connected():
def on_connected(self):
log(INFO, f"{runtime_utils.get_bot_name()} is Online.", origin=L_STARTUP)

@staticmethod
def sound_received(user, audio_chunk):
def sound_received(self, audio_data):
user = audio_data[0]
audio_chunk = audio_data[1]
if audioop.rms(audio_chunk.pcm, 2) > global_settings.vlc_interface.status['ducking_threshold'] and global_settings.vlc_interface.status['duck_audio']:
global_settings.vlc_interface.audio_utilities.duck_volume()
global_settings.vlc_interface.status['duck_start'] = time()
Expand Down
49 changes: 49 additions & 0 deletions JJMumbleBot/core/callback_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from JJMumbleBot.settings import global_settings
from pymumble_py3.constants import PYMUMBLE_CLBK_USERCREATED, PYMUMBLE_CLBK_CONNECTED, PYMUMBLE_CLBK_SOUNDRECEIVED, \
PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, PYMUMBLE_CLBK_DISCONNECTED, PYMUMBLE_CLBK_CHANNELUPDATED, \
PYMUMBLE_CLBK_CHANNELREMOVED, PYMUMBLE_CLBK_CHANNELCREATED, PYMUMBLE_CLBK_USERREMOVED, PYMUMBLE_CLBK_USERUPDATED


class CallbackService:
def __init__(self):
pass

@staticmethod
def message_received(text, remote_cmd=False):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, text, remote_cmd)

@staticmethod
def sound_received(user, audiochunk):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_SOUNDRECEIVED, user, audiochunk)

@staticmethod
def connected():
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_CONNECTED)

@staticmethod
def disconnected():
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_DISCONNECTED)

@staticmethod
def channel_created(channel):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_CHANNELCREATED, channel)

@staticmethod
def channel_updated(channel, modified_params):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_CHANNELUPDATED, channel, modified_params)

@staticmethod
def channel_removed(channel):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_CHANNELREMOVED, channel)

@staticmethod
def user_created(user):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_USERCREATED, user)

@staticmethod
def user_updated(user, modified_params):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_USERUPDATED, user, modified_params)

@staticmethod
def user_removed(user, message):
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_USERREMOVED, user, message)
35 changes: 34 additions & 1 deletion JJMumbleBot/lib/callbacks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from pymumble_py3 import constants
from JJMumbleBot.lib.utils.print_utils import dprint


class Callbacks(dict):
def __init__(self):
super().__init__()
Expand All @@ -20,11 +24,40 @@ def get_callback(self, callback):

def callback(self, callback, *params):
self[callback](params)
#if self[callback]:
# if self[callback]:
# thr = Thread(target=self[callback], args=params)
# thr.start()


class CoreCallbacks(Callbacks):
def __init__(self):
super().__init__()
self.update({
constants.PYMUMBLE_CLBK_USERCREATED: [],
constants.PYMUMBLE_CLBK_TEXTMESSAGERECEIVED: [],
constants.PYMUMBLE_CLBK_SOUNDRECEIVED: [],
constants.PYMUMBLE_CLBK_CONNECTED: [],
constants.PYMUMBLE_CLBK_CHANNELCREATED: [],
constants.PYMUMBLE_CLBK_CHANNELREMOVED: [],
constants.PYMUMBLE_CLBK_CHANNELUPDATED: [],
constants.PYMUMBLE_CLBK_USERREMOVED: [],
constants.PYMUMBLE_CLBK_DISCONNECTED: []
})

def register_callback(self, callback, dest: list):
self[callback] = dest

def append_to_callback(self, callback, method):
self[callback].append(method)

def callback(self, callback, *params):
for clbk in self[callback]:
try:
clbk(params)
except Exception as e:
dprint(e)


class CommandCallbacks(dict):
def __init__(self):
super().__init__()
Expand Down
3 changes: 2 additions & 1 deletion JJMumbleBot/lib/helpers/bot_service_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from JJMumbleBot.lib.utils.plugin_utils import PluginUtilityService
from JJMumbleBot.lib.resources.strings import *
from JJMumbleBot.lib.utils.logging_utils import log
from JJMumbleBot.lib.callbacks import Callbacks, CommandCallbacks
from JJMumbleBot.lib.callbacks import Callbacks, CommandCallbacks, CoreCallbacks


class BotServiceHelper:
Expand All @@ -32,6 +32,7 @@ def initialize_settings():
global_settings.mtd_callbacks = Callbacks()
global_settings.cmd_callbacks = CommandCallbacks()
global_settings.plugin_callbacks = Callbacks()
global_settings.core_callbacks = CoreCallbacks()

runtime_settings.tick_rate = float(global_settings.cfg[C_MAIN_SETTINGS][P_CMD_TICK_RATE])
runtime_settings.cmd_hist_lim = int(global_settings.cfg[C_MAIN_SETTINGS][P_CMD_MULTI_LIM])
Expand Down
2 changes: 1 addition & 1 deletion JJMumbleBot/lib/resources/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,5 @@
###########################################################################
# BOT META INFORMATION STRINGS
META_NAME = "JJMumbleBot"
META_VERSION = "4.0.0"
META_VERSION = "4.1.0"
###########################################################################
6 changes: 5 additions & 1 deletion JJMumbleBot/plugins/extensions/sound_board/metadata.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Plugin Information]
PluginVersion = 4.0.0
PluginVersion = 4.1.0
PluginName = Sound Board
PluginDescription = The Sound Board plugin allows users to play saved sound clips in the channel.
PluginLanguage = EN
Expand All @@ -24,6 +24,10 @@ MaxSearchResults = 5
; The fuzzy-search threshold between 0 to 100. Higher thresholds result in fewer, but more accurate search results. (default=80)
; A search threshold of 0 includes all files.
FuzzySearchThreshold = 80
; Play a selected audio clip when a user joins the server.
PlayAudioClipOnUserJoin = False
; If PlayAudioClipOnUserJoin is set to True, specify the track name (without file extension) below.
AudioClipToPlayOnUserJoin =
; List commands that need the core thread to wait for completion.
; This may include processes that require multiple commands in succession.
; For example: [Youtube Plugin - !yt -> !p] process requires 2 commands in that order.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
P_ENABLE_QUEUE = 'EnableQueue'
P_MAX_SEARCH_RESULTS = 'MaxSearchResults'
P_FUZZY_SEARCH_THRESHOLD = 'FuzzySearchThreshold'
P_PLAY_AUDIO_CLIP_ON_USER_JOIN = 'PlayAudioClipOnUserJoin'
P_AUDIO_CLIP_TO_PLAY_ON_USER_JOIN = 'AudioClipToPlayOnUserJoin'
37 changes: 36 additions & 1 deletion JJMumbleBot/plugins/extensions/sound_board/sound_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
from JJMumbleBot.lib.utils.runtime_utils import get_command_token
from JJMumbleBot.lib.utils import dir_utils
from JJMumbleBot.lib.vlc.vlc_api import TrackType, TrackInfo
from JJMumbleBot.lib.utils.runtime_utils import get_bot_name
from os import path
import random
from datetime import datetime, timedelta
from datetime import datetime
from bs4 import BeautifulSoup


Expand All @@ -27,6 +28,7 @@ def __init__(self):
dir_utils.make_directory(f'{gs.cfg[C_MEDIA_SETTINGS][P_TEMP_MED_DIR]}/{self.plugin_name}/')
sbu_settings.sound_board_metadata = self.metadata
sbu_settings.plugin_name = self.plugin_name
self.register_callbacks()
rprint(
f"{self.metadata[C_PLUGIN_INFO][P_PLUGIN_NAME]} v{self.metadata[C_PLUGIN_INFO][P_PLUGIN_VERS]} Plugin Initialized.")

Expand All @@ -38,6 +40,39 @@ def quit(self):
dprint(f"Exiting {self.plugin_name} plugin...", origin=L_SHUTDOWN)
log(INFO, f"Exiting {self.plugin_name} plugin...", origin=L_SHUTDOWN)

def register_callbacks(self):
from pymumble_py3.constants import PYMUMBLE_CLBK_USERCREATED
if self.metadata.getboolean(C_PLUGIN_SET, P_PLAY_AUDIO_CLIP_ON_USER_JOIN, fallback=False):
gs.core_callbacks.append_to_callback(PYMUMBLE_CLBK_USERCREATED, self.on_new_user_connected)

def on_new_user_connected(self, user):
if gs.vlc_interface.check_dni(self.plugin_name, quiet=True):
gs.vlc_interface.set_dni(self.plugin_name, self.metadata[C_PLUGIN_INFO][P_PLUGIN_NAME])
else:
return

to_play = self.metadata[C_PLUGIN_SET][P_AUDIO_CLIP_TO_PLAY_ON_USER_JOIN]
if not to_play:
return
audio_clip = sbu.find_file(to_play)
if not path.exists(f"{dir_utils.get_perm_med_dir()}/{self.plugin_name}/{audio_clip}"):
gs.vlc_interface.clear_dni()
return
track_obj = TrackInfo(
uri=f'{dir_utils.get_perm_med_dir()}/{self.plugin_name}/{audio_clip}',
name=to_play,
sender=get_bot_name(),
duration=None,
track_type=TrackType.FILE,
quiet=True
)
gs.vlc_interface.enqueue_track(
track_obj=track_obj,
to_front=False,
quiet=True
)
gs.vlc_interface.play(override=True)

def cmd_sblist(self, data):
internal_list = []
data_actor = gs.mumble_inst.users[data.actor]
Expand Down
2 changes: 2 additions & 0 deletions JJMumbleBot/settings/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
# Command Queue
cmd_queue = None
# Callbacks
clbk_service = None
cmd_callbacks = None
mtd_callbacks = None
plugin_callbacks = None
core_callbacks = None
# Aliases
aliases = {}
# Initialized Plugins.
Expand Down
3 changes: 2 additions & 1 deletion JJMumbleBot/web/web_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from JJMumbleBot.lib import monitor_service
from JJMumbleBot.lib.utils.web_utils import RemoteTextMessage
from JJMumbleBot.lib.utils.runtime_utils import check_up_time, get_bot_name
from pymumble_py3.constants import PYMUMBLE_CLBK_TEXTMESSAGERECEIVED
import json
from os import urandom

Expand Down Expand Up @@ -45,7 +46,7 @@ def post_message():
session=global_settings.mumble_inst.users.myself['session'],
message=content,
actor=global_settings.mumble_inst.users.myself['session'])
global_settings.bot_service.message_received(text=text, remote_cmd=True)
global_settings.core_callbacks.callback(PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, text, True)
# print(text.message)
return content

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ A plugin-based All-In-One mumble bot solution in python 3.7+ with extensive feat
- <b>Multi-Threaded Command Processing</b> - Commands in the queue are handled in multiple threads for faster processing.
- <b>Reconfigurable Command Privileges</b> - The user privileges required to execute commands can be completely reconfigured.
- <b>User Privileges System</b> - Set user privileges to server users to limit the command usage on a per-user basis.
- <b>Extensive Callback System</b> - Create custom callbacks to mumble server events and more.

## Screenshots
#### Audio Interface System (youtube plugin, sound board plugin, etc)
Expand Down

0 comments on commit dfb1eed

Please sign in to comment.