From: Daniel Karbach <daniel.karbach@localhorst.tv>
Date: Thu, 2 Mar 2023 14:11:57 +0000 (+0100)
Subject: first simple twitch commands
X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=cde5d79cf2f09d61fa7b181cd3a1a19050a4aeb3;p=alttp.git

first simple twitch commands
---

diff --git a/app/Models/Channel.php b/app/Models/Channel.php
index 8b5ff90..0536aef 100644
--- a/app/Models/Channel.php
+++ b/app/Models/Channel.php
@@ -9,6 +9,13 @@ class Channel extends Model
 {
 	use HasFactory;
 
+	public function getCurrentEpisode() {
+		return $this->episodes()
+			->where('start', '<', now()->subMinutes(10))
+			->orderBy('start', 'DESC')
+			->first();
+	}
+
 	public function episodes() {
 		return $this->belongsToMany(Episode::class)
 			->using(Restream::class)
@@ -20,6 +27,7 @@ class Channel extends Model
 	}
 
 	protected $casts = [
+		'chat_commands' => 'array',
 		'languages' => 'array',
 	];
 
diff --git a/app/Models/EpisodeCrew.php b/app/Models/EpisodeCrew.php
index d06276e..04bec5c 100644
--- a/app/Models/EpisodeCrew.php
+++ b/app/Models/EpisodeCrew.php
@@ -21,6 +21,31 @@ class EpisodeCrew extends Model
 		return $this->belongsTo(User::class);
 	}
 
+	public function getName() {
+		if (!empty($this->name_override)) {
+			return $this->name_override;
+		}
+		if ($this->user) {
+			if (!empty($this->user->nickname)) {
+				return $this->user->nickname;
+			}
+			if (!empty($this->user->username)) {
+				return $this->user->username;
+			}
+		}
+		return '';
+	}
+
+	public function getStreamLink() {
+		if (!empty($this->stream_override)) {
+			return $this->stream_override;
+		}
+		if ($this->user && !empty($this->user->stream_link)) {
+			return $this->user->stream_link;
+		}
+		return '';
+	}
+
 	protected $casts = [
 		'confirmed' => 'boolean',
 		'user_id' => 'string',
diff --git a/app/Models/EpisodePlayer.php b/app/Models/EpisodePlayer.php
index 1125701..26ccc04 100644
--- a/app/Models/EpisodePlayer.php
+++ b/app/Models/EpisodePlayer.php
@@ -17,6 +17,31 @@ class EpisodePlayer extends Model
 		return $this->belongsTo(User::class);
 	}
 
+	public function getName() {
+		if (!empty($this->name_override)) {
+			return $this->name_override;
+		}
+		if ($this->user) {
+			if (!empty($this->user->nickname)) {
+				return $this->user->nickname;
+			}
+			if (!empty($this->user->username)) {
+				return $this->user->username;
+			}
+		}
+		return '';
+	}
+
+	public function getStreamLink() {
+		if (!empty($this->stream_override)) {
+			return $this->stream_override;
+		}
+		if ($this->user && !empty($this->user->stream_link)) {
+			return $this->user->stream_link;
+		}
+		return '';
+	}
+
 	protected $casts = [
 		'user_id' => 'string',
 	];
diff --git a/app/TwitchBot/ChatCommand.php b/app/TwitchBot/ChatCommand.php
new file mode 100644
index 0000000..feea8ab
--- /dev/null
+++ b/app/TwitchBot/ChatCommand.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace App\TwitchBot;
+
+use App\Models\Channel;
+
+abstract class ChatCommand {
+
+	public static function create(TwitchBot $bot, Channel $channel, $config) {
+		$cmd = null;
+		switch ($config['command']) {
+			case 'crew':
+				$cmd = new CrewCommand();
+				break;
+			case 'runner':
+				$cmd = new RunnerCommand();
+				break;
+			default:
+				throw new \Exception('command '.$str.' not found');
+		}
+		$cmd->bot = $bot;
+		$cmd->channel = $channel;
+		$cmd->config = $config;
+		return $cmd;
+	}
+
+	public abstract function execute($args);
+
+	protected function getBooleanConfig($name, $default = false) {
+		return array_key_exists($name, $this->config) ? $this->config[$name] : $default;
+	}
+
+	protected function messageChannel($str) {
+		$msg = IRCMessage::privmsg($this->channel->twitch_chat, $str);
+		$this->bot->sendIRCMessage($msg);
+	}
+
+	protected $bot;
+	protected $channel;
+	protected $config;
+
+}
+
+?>
diff --git a/app/TwitchBot/CrewCommand.php b/app/TwitchBot/CrewCommand.php
new file mode 100644
index 0000000..2fd0426
--- /dev/null
+++ b/app/TwitchBot/CrewCommand.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace App\TwitchBot;
+
+class CrewCommand extends ChatCommand {
+
+	public function execute($args) {
+		$episode = $this->channel->getCurrentEpisode();
+		if (!$episode) return;
+		$links = [];
+		foreach ($episode->crew as $crew) {
+			$link = $crew->getStreamLink();
+			if (empty($link)) {
+				$link = $crew->getName();
+			}
+			if (!empty($link)) {
+				if (!isset($links[$crew->role])) {
+					$links[$crew->role] = [];
+				}
+				$links[$crew->role][] = $link;
+			}
+		}
+		$parts = [];
+		if (!empty($links['commentary']) && $this->getBooleanConfig('commentary', true)) {
+			$parts[] = 'Kommentar: '.implode(' ', $links['commentary']);
+		}
+		if (!empty($links['tracking']) && $this->getBooleanConfig('tracking', true)) {
+			$parts[] = 'Tracking: '.implode(' ', $links['tracking']);
+		}
+		if (!empty($links['setup']) && $this->getBooleanConfig('setup', false)) {
+			$parts[] = 'Setup: '.implode(' ', $links['setup']);
+		}
+		if (!empty($parts)) {
+			$message = implode(' ', $parts);
+			$this->messageChannel($message);
+		}
+	}
+
+}
+
+?>
diff --git a/app/TwitchBot/IRCMessage.php b/app/TwitchBot/IRCMessage.php
index c0acfbb..00217dc 100644
--- a/app/TwitchBot/IRCMessage.php
+++ b/app/TwitchBot/IRCMessage.php
@@ -132,6 +132,14 @@ class IRCMessage {
 		return $msg;
 	}
 
+	public static function privmsg($target, $message) {
+		$msg = new IRCMessage();
+		$msg->command = 'PRIVMSG';
+		$msg->params[] = $target;
+		$msg->params[] = $message;
+		return $msg;
+	}
+
 	public function getPrivMsgTarget() {
 		if (!empty($this->params)) {
 			return $this->params[0];
diff --git a/app/TwitchBot/RunnerCommand.php b/app/TwitchBot/RunnerCommand.php
new file mode 100644
index 0000000..2cc41b2
--- /dev/null
+++ b/app/TwitchBot/RunnerCommand.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace App\TwitchBot;
+
+class RunnerCommand extends ChatCommand {
+
+	public function execute($args) {
+		$episode = $this->channel->getCurrentEpisode();
+		if (!$episode) return;
+		$links = [];
+		foreach ($episode->players as $player) {
+			$link = $player->getStreamLink();
+			if (empty($link)) {
+				$link = $player->getName();
+			}
+			if (!empty($link)) {
+				$links[] = $link;
+			}
+		}
+		$message = 'Runner: '.implode(' ', $links);
+		$this->messageChannel($message);
+	}
+
+}
+
+?>
diff --git a/app/TwitchBot/TwitchBot.php b/app/TwitchBot/TwitchBot.php
index 3ca2db9..3cba5d9 100644
--- a/app/TwitchBot/TwitchBot.php
+++ b/app/TwitchBot/TwitchBot.php
@@ -15,7 +15,7 @@ class TwitchBot {
 
 	public function __construct() {
 		$this->logger = new Logger('TwitchBot');
-		$this->logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
+		$this->logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO));
 
 		$this->token = TwitchToken::firstWhere('nick', 'localhorsttv');
 		if (!$this->token) {
@@ -122,7 +122,20 @@ class TwitchBot {
 		if ($text[0] != '!') return;
 		$channel = Channel::firstWhere('twitch_chat', '=', $target);
 		if (!$channel) return;
-		$this->logger->info('got command '.$text.' on channel '.$channel->title);
+		$this->handleChatCommand($channel, $msg);
+	}
+
+	public function handleChatCommand(Channel $channel, IRCMessage $msg) {
+		$cmd = explode(' ', ltrim($msg->getText(), '!'), 2);
+		if (!isset($channel->chat_commands[$cmd[0]])) return;
+		$config = $channel->chat_commands[$cmd[0]];
+		$this->logger->info('got command '.$cmd[0].' on channel '.$channel->title);
+		try {
+			$command = ChatCommand::create($this, $channel, $config);
+			$command->execute($cmd[1] ?? '');
+		} catch (\Exception $e) {
+			$this->logger->warning('error executing command '.$cmd[0].' on channel '.$channel->title.': '.$e->getMessage());
+		}
 	}
 
 	public function login() {
diff --git a/database/migrations/2023_03_01_143514_channel_twitch_chat.php b/database/migrations/2023_03_01_143514_channel_twitch_chat.php
index b9d3901..6429063 100644
--- a/database/migrations/2023_03_01_143514_channel_twitch_chat.php
+++ b/database/migrations/2023_03_01_143514_channel_twitch_chat.php
@@ -25,7 +25,7 @@ return new class extends Migration
 	 */
 	public function down()
 	{
-		Schema::table('events', function(Blueprint $table) {
+		Schema::table('channels', function(Blueprint $table) {
 			$table->dropColumn('twitch_chat');
 		});
 	}
diff --git a/database/migrations/2023_03_02_084058_channel_commands.php b/database/migrations/2023_03_02_084058_channel_commands.php
new file mode 100644
index 0000000..df639b1
--- /dev/null
+++ b/database/migrations/2023_03_02_084058_channel_commands.php
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+	/**
+	 * Run the migrations.
+	 *
+	 * @return void
+	 */
+	public function up()
+	{
+		Schema::table('channels', function(Blueprint $table) {
+			$table->text('chat_commands')->default('{}');
+		});
+	}
+
+	/**
+	 * Reverse the migrations.
+	 *
+	 * @return void
+	 */
+	public function down()
+	{
+		Schema::table('channels', function(Blueprint $table) {
+			$table->dropColumn('chat_commands');
+		});
+	}
+};