diff --git a/README.md b/README.md
index 9b76b8a..e90a946 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ To add a bot you need to link your app with the bot twitch account.
1. Open `${APP_URL}/connection/bot/redirect` with your laravel-app.
2. Login into your Twitch-Bot account with your Twitch Application.
-3. After redirect you need to manually connect your laravel-Account with a bot.
+3. After redirect, you need to manually connect your laravel-Account with a bot.
4. Open Your Database table "bot_connections" and connect your bot with your user.
5. Restart the Bot Artisan Bot
diff --git a/composer.json b/composer.json
index 7a0f50f..b822a89 100644
--- a/composer.json
+++ b/composer.json
@@ -12,6 +12,7 @@
"homepage": "https://github.com/redbeed/openoverlay",
"keywords": ["Laravel", "OpenOverlay", "twitch", "Eventsub", "Bot", "IRC"],
"require": {
+ "php": "^8.0",
"illuminate/support": "~8|~9",
"guzzlehttp/guzzle": "^7.2",
"ext-json": "*",
@@ -23,9 +24,13 @@
"require-dev": {
"phpunit/phpunit": "~9.0",
"orchestra/testbench": "~5|~6",
- "nunomaduro/phpinsights": "^1.14"
+ "nunomaduro/phpinsights": "^1.14",
+ "laravel/pint": "^0.2.3"
},
"autoload": {
+ "files": [
+ "src/Support/helpers.php"
+ ],
"psr-4": {
"Redbeed\\OpenOverlay\\": "src/",
"Redbeed\\OpenOverlay\\Database\\": "database/"
@@ -47,5 +52,10 @@
}
},
"minimum-stability": "dev",
- "prefer-stable": true
+ "prefer-stable": true,
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ }
}
diff --git a/config/openoverlay.php b/config/openoverlay.php
index 8efce82..18f9429 100644
--- a/config/openoverlay.php
+++ b/config/openoverlay.php
@@ -33,12 +33,12 @@
* You can use :username, :twitchUrl and :gameName for your message.
*/
\Redbeed\OpenOverlay\Listeners\AutoShoutOutRaid::class => [
- 'message' => 'Follow :username over at :twitchUrl. They were last playing :gameName'
+ 'message' => 'Follow :username over at :twitchUrl. They were last playing :gameName',
],
\Redbeed\OpenOverlay\Support\ViewerInChat::class => [
- 'reset' => -1
- ]
+ 'reset' => -1,
+ ],
],
'webhook' => [
@@ -55,7 +55,7 @@
/**
* Your personal and unique secret is used to validate a twitch callback
- * If you change your secret all previous configures webhook callbacks will be end as invalid
+ * If you change your secret all previous configures webhook callbacks will be ended as invalid
*/
'secret' => env('OVERLAY_SECRET'),
@@ -72,20 +72,8 @@
],
'bot' => [
- 'commands' => [
-
- 'simple' => [
- '!hello' => 'Hello %username%! How are you doing?',
- ],
-
- 'advanced' => [
- \Redbeed\OpenOverlay\ChatBot\Commands\HelloWorldBotCommand::class,
- \Redbeed\OpenOverlay\ChatBot\Commands\ShoutOutBotCommand::class,
- ]
- ],
-
'schedules' => [
\Redbeed\OpenOverlay\Console\Scheduling\MadeWithChatBotScheduling::class,
- ]
- ]
+ ],
+ ],
];
diff --git a/database/Factories/EventSubEventsFactory.php b/database/Factories/EventSubEventsFactory.php
index 895de5e..0d895ae 100644
--- a/database/Factories/EventSubEventsFactory.php
+++ b/database/Factories/EventSubEventsFactory.php
@@ -7,11 +7,10 @@
class EventSubEventsFactory extends Factory
{
-
protected $model = EventSubEvents::class;
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
public function definition()
{
diff --git a/database/migrations/2020_12_17_000001_create_user_bots_enabled_table.php b/database/migrations/2020_12_17_000001_create_user_bots_enabled_table.php
index 44f7d76..ced6730 100644
--- a/database/migrations/2020_12_17_000001_create_user_bots_enabled_table.php
+++ b/database/migrations/2020_12_17_000001_create_user_bots_enabled_table.php
@@ -14,7 +14,6 @@ class CreateUserBotsEnabledTable extends Migration
public function up()
{
Schema::create('users_bots_enabled', function (Blueprint $table) {
-
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('bot_id');
$table->timestamps();
diff --git a/routes/openoverlay.php b/routes/openoverlay.php
index 78e8990..ae67d12 100644
--- a/routes/openoverlay.php
+++ b/routes/openoverlay.php
@@ -1,7 +1,6 @@
group(function () {
-
Route::middleware(['web', 'auth'])->group(function () {
-
Route::get('/redirect')->uses([AuthController::class, 'redirect'])
->name('connection.redirect');
@@ -27,7 +24,6 @@
Route::get('/callback')->uses([AppTokenController::class, 'handleProviderCallback'])
->name('connection.app-token.callback');
-
});
// prefix: /connection/bot
@@ -37,7 +33,6 @@
Route::get('/callback')->uses([BotAuthController::class, 'handleProviderCallback'])
->name('connection.bot.callback');
-
});
});
@@ -45,6 +40,5 @@
Route::any('/webhook')->uses([WebhookController::class, 'handleProviderCallback'])
->name('connection.webhook');
});
-
});
});
diff --git a/src/Actions/RegisterUserTwitchWebhooks.php b/src/Actions/RegisterUserTwitchWebhooks.php
index d4f479f..30b4921 100644
--- a/src/Actions/RegisterUserTwitchWebhooks.php
+++ b/src/Actions/RegisterUserTwitchWebhooks.php
@@ -36,7 +36,7 @@ public static function registerAll(Connection $connection, bool $clearBeforeRegi
public function clearBroadcasterSubscriptions()
{
- $this->apiClient->deleteSubByBroadcasterId((string)$this->connection->service_user_id);
+ $this->apiClient->deleteSubByBroadcasterId((string) $this->connection->service_user_id);
}
public function register(string $type): bool
@@ -61,7 +61,7 @@ public function register(string $type): bool
private function registerCondition($type): array
{
- $broadcasterId = (string)$this->connection->service_user_id;
+ $broadcasterId = (string) $this->connection->service_user_id;
if ($type === 'channel.raid') {
return ['to_broadcaster_user_id' => $broadcasterId];
diff --git a/src/Automations/Actions/TwitchChatBotMessage.php b/src/Automations/Actions/TwitchChatBotMessage.php
new file mode 100644
index 0000000..7ae3653
--- /dev/null
+++ b/src/Automations/Actions/TwitchChatBotMessage.php
@@ -0,0 +1,35 @@
+message = $message;
+ }
+
+ public function handle()
+ {
+ Artisan::queue(SendMessageCommand::class, [
+ 'userId' => $this->getUser()->id,
+ '--botId' => $this->getBot()->id,
+ 'message' => $this->replaceInString($this->message),
+ ]);
+ }
+}
diff --git a/src/Automations/Actions/TwitchRandomChatBotMessage.php b/src/Automations/Actions/TwitchRandomChatBotMessage.php
new file mode 100644
index 0000000..c827cba
--- /dev/null
+++ b/src/Automations/Actions/TwitchRandomChatBotMessage.php
@@ -0,0 +1,37 @@
+messages = $messages;
+ }
+
+ public function handle()
+ {
+ Artisan::queue(SendMessageCommand::class, [
+ 'userId' => $this->getUser()->id,
+ '--botId' => $this->getBot()->id,
+ 'message' => $this->replaceInString(Arr::random($this->messages)),
+ ]);
+ }
+}
diff --git a/src/Automations/Actions/UseTwitchChatMessage.php b/src/Automations/Actions/UseTwitchChatMessage.php
new file mode 100644
index 0000000..178d9d4
--- /dev/null
+++ b/src/Automations/Actions/UseTwitchChatMessage.php
@@ -0,0 +1,41 @@
+chatMessage = $chatMessage;
+ }
+
+ public function setBotConnection(BotConnection $botConnection): void
+ {
+ $this->botConnection = $botConnection;
+ }
+
+ public function setUser(User $user): void
+ {
+ $this->user = $user;
+ }
+
+ protected function getBot(): ?BotConnection
+ {
+ return $this->botConnection ?: $this->chatMessage?->bot;
+ }
+
+ protected function getUser(): ?User
+ {
+ return $this->user ?: $this->chatMessage?->channelUser;
+ }
+}
diff --git a/src/Automations/Actions/UseVariables.php b/src/Automations/Actions/UseVariables.php
new file mode 100644
index 0000000..039c13d
--- /dev/null
+++ b/src/Automations/Actions/UseVariables.php
@@ -0,0 +1,60 @@
+variables = array_merge_recursive($this->variables, $variables);
+ }
+
+ public function getVariables($filterName = ''): array
+ {
+ if ($filterName) {
+ return $this->filterVariables($filterName);
+ }
+
+ return $this->variables;
+ }
+
+ private function filterVariables(string $filterName): array
+ {
+ $filtered = [];
+ foreach ($this->variables as $name => $value) {
+ if (str_contains($name, $filterName)) {
+ $filtered[$name] = $value;
+ }
+ }
+
+ return $filtered;
+ }
+
+ protected function replaceInString(string $string): string
+ {
+ return strtr($string, $this->makeReplacements($string));
+ }
+
+ protected function makeReplacements(string $string): array
+ {
+ $replacements = [];
+ foreach ($this->variables as $key => $value) {
+ $keyPattern = ':'.$key;
+
+ if (Str::contains($string, $keyPattern)) {
+ if ($this->variables[$key] instanceof \Closure) {
+ // If the variable is a closure, we will execute it and replace the key with the result.
+ $this->variables[$key] = $this->variables[$key]();
+ }
+
+ $replacements[$keyPattern] = $this->variables[$key];
+ }
+ }
+
+ return $replacements;
+ }
+}
diff --git a/src/Automations/AutomationDispatcher.php b/src/Automations/AutomationDispatcher.php
new file mode 100644
index 0000000..bdf088e
--- /dev/null
+++ b/src/Automations/AutomationDispatcher.php
@@ -0,0 +1,56 @@
+each(function ($handler) use ($trigger) {
+ $this->add($trigger, $handler);
+ });
+
+ return;
+ }
+
+ $this->automations[$trigger][] = $handlerClass;
+ }
+
+ public function getAutomations(?string $triggerClass = null): array
+ {
+ if ($triggerClass) {
+ return $this->automations[$triggerClass] ?? [];
+ }
+
+ return $this->automations;
+ }
+
+ public function trigger(mixed $trigger)
+ {
+ ray('fire '.get_class($trigger));
+
+ if (empty($this->automations[$trigger::class])) {
+ return;
+ }
+
+ foreach ($this->automations[$trigger::class] as $automations) {
+ collect($automations)->each(function ($automation) use ($trigger) {
+ // If it's a closure, execute it
+ if ($automation instanceof Closure) {
+ $automation = $automation($trigger);
+ $automation->handle();
+
+ return;
+ }
+
+ $automation = new $automation($trigger);
+ $automation->handle();
+ });
+ }
+ }
+}
diff --git a/src/Automations/AutomationHandler.php b/src/Automations/AutomationHandler.php
new file mode 100644
index 0000000..1f03c16
--- /dev/null
+++ b/src/Automations/AutomationHandler.php
@@ -0,0 +1,89 @@
+trigger = $trigger;
+ }
+
+ /**
+ * @return Filter[]
+ */
+ public function filters(): array
+ {
+ return [];
+ }
+
+ /**
+ * @return array
+ */
+ public function actions(): array
+ {
+ return [];
+ }
+
+ public function handle()
+ {
+ $variables = [];
+
+ foreach ($this->filters() as $filter) {
+ $response = $filter->handle($this->trigger);
+
+ ray('did i passed? ', $response, $filter->settings());
+
+ if ($response === false) {
+ // Filter failed stop the automation
+ return;
+ }
+
+ $variables = array_merge_recursive(
+ $variables, $filter->variables()
+ );
+ }
+
+ foreach ($this->actions() as $action) {
+ $traits = class_uses($action);
+
+ // Check if the action uses the UsesVariables trait and if so, add the variables to the action
+ if (in_array(UseVariables::class, $traits)) {
+ $action->addVariables($variables);
+ }
+
+ // Check if the action uses the UsesTwitchChatMessage trait and if so, add the message to the action
+ if (in_array(UseTwitchChatMessage::class, $traits) && $this->trigger instanceof TwitchChatMessageTrigger) {
+ $action->setChatMessage($this->trigger->message);
+ }
+
+ $action->handle();
+ }
+ }
+
+ #[ArrayShape(['trigger' => 'string', 'options' => 'array'])]
+ public static function triggerConfig(string $triggerClass, array $options = [])
+ {
+ return ['trigger' => $triggerClass, 'options' => $options];
+ }
+
+ #[ArrayShape(['action' => 'string', 'options' => 'array'])]
+ public static function actionConfig(string $actionClass, array $options = [])
+ {
+ return ['action' => $actionClass, 'options' => $options];
+ }
+}
diff --git a/src/Automations/AutomationsServiceProvider.php b/src/Automations/AutomationsServiceProvider.php
new file mode 100644
index 0000000..023abac
--- /dev/null
+++ b/src/Automations/AutomationsServiceProvider.php
@@ -0,0 +1,42 @@
+app->singleton('automations', function () {
+ return new AutomationDispatcher();
+ });
+
+ $this->booting(function () {
+ $automations = $this->getAutomations();
+
+ foreach ($automations as $trigger => $handler) {
+ Automation::add($trigger, $handler);
+ }
+ });
+ }
+
+ public function getAutomations(): array
+ {
+ return $this->automations;
+ }
+
+ public function boot()
+ {
+ $this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
+ $schedule->call(function () {
+ \automation(new ScheduleTrigger());
+ })->everyMinute();
+ });
+ }
+}
diff --git a/src/Automations/Filters/ChatMessage/ChatMessageContainsFilter.php b/src/Automations/Filters/ChatMessage/ChatMessageContainsFilter.php
new file mode 100644
index 0000000..2fd459d
--- /dev/null
+++ b/src/Automations/Filters/ChatMessage/ChatMessageContainsFilter.php
@@ -0,0 +1,82 @@
+needle = $needle;
+ $this->caseSensitive = $caseSensitive;
+ }
+
+ #[Pure]
+ public function validate(): bool
+ {
+ $message = $this->trigger->message->message;
+ $needle = $this->needle;
+
+ if (! $this->caseSensitive) {
+ $message = Str::lower($message);
+ $needle = Str::lower($needle);
+ }
+
+ return Str::contains($message, $needle);
+ }
+
+ /**
+ * @throws AutomationFilterNotValid
+ */
+ public function validTrigger()
+ {
+ parent::validTrigger();
+
+ if (! ($this->trigger instanceof TwitchChatMessageTrigger)) {
+ throw new AutomationFilterNotValid('Trigger is not valid. Trigger must be instance of TwitchChatMessageTrigger but is '.get_class($this->trigger));
+ }
+ }
+
+ public function variables(): array
+ {
+ return [
+ 'username' => $this->trigger->message->username,
+ 'twitchUrl' => 'https://www.twitch.tv/'.$this->trigger->message->username,
+ 'game' => function () {
+ try {
+ return (new UsersClient())->lastGame($this->trigger->message->username);
+ } catch (ClientException) {
+ return '';
+ }
+ },
+ ];
+ }
+
+ public function settings(): array
+ {
+ return [
+ 'needle' => $this->needle,
+ 'caseSensitive' => $this->caseSensitive,
+ ];
+ }
+}
diff --git a/src/Automations/Filters/ChatMessage/ChatMessageContainsWithPatternFilter.php b/src/Automations/Filters/ChatMessage/ChatMessageContainsWithPatternFilter.php
new file mode 100644
index 0000000..f8c4f3d
--- /dev/null
+++ b/src/Automations/Filters/ChatMessage/ChatMessageContainsWithPatternFilter.php
@@ -0,0 +1,108 @@
+needle = $needle;
+ $this->regexPatterns = $regexPatterns;
+ $this->caseSensitive = $needleCaseSensitive;
+ }
+
+ public function validate(): bool
+ {
+ $message = $this->trigger->message->message;
+ $needle = $this->needle;
+
+ if (! $this->caseSensitive) {
+ $message = Str::lower($message);
+ $needle = Str::lower($needle);
+ }
+
+ if (! Str::contains($message, $needle)) {
+ return false;
+ }
+
+ return preg_match($this->regex(), $this->trigger->message->message);
+ }
+
+ private function regex(): string
+ {
+ $regex = collect($this->regexPatterns)
+ ->mapWithKeys(function ($regex, $key) {
+ return [$key => '(?<'.$key.'>'.$regex.')'];
+ })
+ ->prepend($this->needle)
+ ->implode(' ');
+
+ return '/'.$regex.'/';
+ }
+
+ private function matches(): array
+ {
+ $matches = [];
+ preg_match($this->regex(), $this->trigger->message->message, $matches);
+
+ return array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
+ }
+
+ /**
+ * @throws AutomationFilterNotValid
+ */
+ public function validTrigger()
+ {
+ parent::validTrigger();
+
+ if (! ($this->trigger instanceof TwitchChatMessageTrigger)) {
+ throw new AutomationFilterNotValid('Trigger is not valid. Trigger must be instance of TwitchChatMessageTrigger but is '.get_class($this->trigger));
+ }
+ }
+
+ public function variables(): array
+ {
+ return array_merge_recursive([
+ 'username' => $this->trigger->message->username,
+ 'game' => function () {
+ try {
+ return (new UsersClient())->lastGame($this->trigger->message->username);
+ } catch (ClientException) {
+ return '';
+ }
+ },
+ ], $this->matches());
+ }
+
+ public function settings(): array
+ {
+ return [
+ 'needle' => $this->needle,
+ 'regexPatterns' => $this->regexPatterns,
+ 'caseSensitive' => $this->caseSensitive,
+ ];
+ }
+}
diff --git a/src/Automations/Filters/Filter.php b/src/Automations/Filters/Filter.php
new file mode 100644
index 0000000..50387bf
--- /dev/null
+++ b/src/Automations/Filters/Filter.php
@@ -0,0 +1,57 @@
+trigger = $trigger;
+ $this->validTrigger();
+
+ return $this->validate();
+ }
+
+ public function validate(): bool
+ {
+ return true;
+ }
+
+ public function settings(): array
+ {
+ return [];
+ }
+
+ /**
+ * @throws AutomationFilterNotValid
+ */
+ public function validTrigger()
+ {
+ if (! ($this->trigger instanceof Trigger)) {
+ throw new AutomationFilterNotValid('Trigger is not valid. Trigger must be instance of Trigger but is '.get_class($this->trigger));
+ }
+ }
+
+ public function variables(): array
+ {
+ return [];
+ }
+}
diff --git a/src/Automations/Filters/FrequencyFilter.php b/src/Automations/Filters/FrequencyFilter.php
new file mode 100644
index 0000000..22ad499
--- /dev/null
+++ b/src/Automations/Filters/FrequencyFilter.php
@@ -0,0 +1,32 @@
+trigger instanceof ScheduleTrigger)) {
+ throw new AutomationFilterNotValid('Trigger is not valid. Trigger must be instance of ScheduleTrigger but is '.get_class($this->trigger));
+ }
+ }
+
+ public function validate(): bool
+ {
+ return (new CronExpression($this->expression))->isDue($this->trigger->date->toDateTimeString());
+ }
+}
diff --git a/src/Automations/Filters/Twitch/ChannelStatus.php b/src/Automations/Filters/Twitch/ChannelStatus.php
new file mode 100644
index 0000000..d28f0a2
--- /dev/null
+++ b/src/Automations/Filters/Twitch/ChannelStatus.php
@@ -0,0 +1,70 @@
+connection = $userConnection;
+ }
+
+ private function setOnlineStatus(string $status): self
+ {
+ $this->onlineStatus = $status;
+
+ return $this;
+ }
+
+ public function isOnline(): self
+ {
+ return $this->setOnlineStatus(self::IS_ONLINE);
+ }
+
+ public function isOffline(): self
+ {
+ return $this->setOnlineStatus(self::IS_OFFLINE);
+ }
+
+ protected function validateOnlineStatus(): bool
+ {
+ if ($this->onlineStatus === null) {
+ return true;
+ }
+
+ $currentStatus = StreamerOnline::isOnline($this->connection->service_user_id) ? self::IS_ONLINE : self::IS_OFFLINE;
+
+ return $currentStatus === $this->onlineStatus;
+ }
+
+ public function validate(): bool
+ {
+ if (! $this->validateOnlineStatus()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function settings(): array
+ {
+ return [
+ 'onlineStatus' => $this->onlineStatus,
+ ];
+ }
+}
diff --git a/src/Automations/Triggers/ScheduleTrigger.php b/src/Automations/Triggers/ScheduleTrigger.php
new file mode 100644
index 0000000..6c69602
--- /dev/null
+++ b/src/Automations/Triggers/ScheduleTrigger.php
@@ -0,0 +1,19 @@
+date = now();
+ }
+}
diff --git a/src/Automations/Triggers/Trigger.php b/src/Automations/Triggers/Trigger.php
new file mode 100644
index 0000000..3273484
--- /dev/null
+++ b/src/Automations/Triggers/Trigger.php
@@ -0,0 +1,27 @@
+options = $options;
+ }
+}
diff --git a/src/Automations/Triggers/TwitchChatMessageTrigger.php b/src/Automations/Triggers/TwitchChatMessageTrigger.php
new file mode 100644
index 0000000..ce543b4
--- /dev/null
+++ b/src/Automations/Triggers/TwitchChatMessageTrigger.php
@@ -0,0 +1,29 @@
+message = $message;
+ }
+
+ public function valid(): bool
+ {
+ if (empty($this->options['message'])) {
+ return true;
+ }
+
+ return Str::contains($this->message->message, $this->options['message']);
+ }
+}
diff --git a/src/ChatBot/Commands/BotCommand.php b/src/ChatBot/Commands/BotCommand.php
deleted file mode 100644
index 71955b7..0000000
--- a/src/ChatBot/Commands/BotCommand.php
+++ /dev/null
@@ -1,126 +0,0 @@
-connection = $connectionHandler;
- }
-
- public function handle(ChatMessage $chatMessage)
- {
- if ($this->messageValid($chatMessage->message) === false) {
- return;
- }
-
- // build & check parameters
- $this->buildParameters($chatMessage->message);
- if ($this->parametersValid() === false) {
- return;
- }
-
- $this->connection->sendChatMessage(
- $chatMessage->channel,
- $this->response($chatMessage)
- );
- }
-
- protected function parametersValid(): bool
- {
- $keys = $this->parametersKeys();
-
- return count($keys) === count($this->parameters);
- }
-
- protected function buildParameters(string $message)
- {
- $keys = $this->parametersKeys();
- if (count($keys) <= 0) {
- return;
- }
-
- $valuesOnly = explode(' ', $message, 2);
- if (count($valuesOnly) !== 2) {
- return;
- }
-
- $values = explode(' ', $valuesOnly[1], count($keys));
- foreach ($values as $valueKey => $value) {
- $this->parameters[$keys[$valueKey]] = $value;
- }
- }
-
- protected function parameter(string $key): ?string
- {
- if (isset($this->parameters[$key])) {
- return $this->parameters[$key];
- }
-
- return null;
- }
-
- public function parametersKeys(): array
- {
- preg_match_all("/\{(.+?)\}/m", $this->signature, $matches);
- if (count($matches) < 2) {
- return [];
- }
-
- return $matches[1];
- }
-
- public function response(ChatMessage $chatMessage): string
- {
- return '';
- }
-
- protected function command(): string
- {
- return head(explode(' ', $this->signature));
- }
-
- protected function messageValid(string $message): bool
- {
- if ($this->messageStartsWith($message, $this->command())) {
- return true;
- }
-
- if (is_array($this->aliasCommands) && count($this->aliasCommands)) {
- foreach ($this->aliasCommands as $aliasCommand) {
- if ($this->messageStartsWith($message, $aliasCommand)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- protected function messageStartsWith(string $message, string $command): bool
- {
- // perfect match
- if (trim($command) === trim($message)) {
- return true;
- }
-
- // match with space
- return substr(trim($message), 0, strlen(trim($command) . ' ')) === trim($command) . ' ';
- }
-}
diff --git a/src/ChatBot/Commands/HelloWorldBotCommand.php b/src/ChatBot/Commands/HelloWorldBotCommand.php
deleted file mode 100644
index 1e49dd2..0000000
--- a/src/ChatBot/Commands/HelloWorldBotCommand.php
+++ /dev/null
@@ -1,19 +0,0 @@
-username,
- 'It is ' . Carbon::now()->toString() . '... I think',
- ]);
- }
-}
diff --git a/src/ChatBot/Commands/ShoutOutBotCommand.php b/src/ChatBot/Commands/ShoutOutBotCommand.php
deleted file mode 100644
index e1ff204..0000000
--- a/src/ChatBot/Commands/ShoutOutBotCommand.php
+++ /dev/null
@@ -1,48 +0,0 @@
-parameter('username'), '@');
-
- $usersClient = new UsersClient();
- try {
- $users = $usersClient->byUsername($username);
- } catch (ClientException $exception) {
- return '';
- }
-
- $user = head($users['data']);
- if ($user['login'] !== strtolower($username)) {
- return '';
- }
-
- $response = [
- 'Don“t forget to checkout ' . $user['display_name'] . ' www.twitch.tv/' . $user['login']
- ];
-
- try {
- $channelClient = new ChannelsClient();
- $channels = $channelClient->get($user['id']);
- $channel = head($channels['data']);
-
- if (!empty($channel['game_id'])) {
- $response[] = '- last playing "' . $channel['game_name'] . '"';
- }
- } catch (ClientException $exception) {
- // ignore
- }
-
- return implode(' ', $response);
- }
-}
diff --git a/src/ChatBot/Commands/SimpleBotCommands.php b/src/ChatBot/Commands/SimpleBotCommands.php
deleted file mode 100644
index e97c0e4..0000000
--- a/src/ChatBot/Commands/SimpleBotCommands.php
+++ /dev/null
@@ -1,49 +0,0 @@
-simpleCommands = config('openoverlay.bot.commands.simple');
- }
-
- public function handle(ChatMessage $chatMessage)
- {
- foreach ($this->simpleCommands as $command => $responseMessage) {
- $this->handleSimpleCommand($chatMessage, $command, $responseMessage);
- }
- }
-
- public function handleSimpleCommand(ChatMessage $chatMessage, string $command, string $responseMessage): void
- {
- if ($this->messageStartsWith($chatMessage->message, $command) === false) {
- return;
- }
-
- $this->connection->sendChatMessage(
- $chatMessage->channel,
- $this->responseSimpleCommand($chatMessage, $responseMessage)
- );
- }
-
- public function responseSimpleCommand(ChatMessage $chatMessage, string $message): string
- {
- $replace = [
- '%username%' => $chatMessage->username,
- ];
-
- return str_replace(array_keys($replace), array_values($replace), $message);
- }
-}
diff --git a/src/ChatBot/Twitch/ChatMessage.php b/src/ChatBot/Twitch/ChatMessage.php
index 98e9dd7..349c0a7 100644
--- a/src/ChatBot/Twitch/ChatMessage.php
+++ b/src/ChatBot/Twitch/ChatMessage.php
@@ -2,38 +2,46 @@
namespace Redbeed\OpenOverlay\ChatBot\Twitch;
+use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
+use Redbeed\OpenOverlay\Models\BotConnection;
use Redbeed\OpenOverlay\Models\Twitch\Emote;
class ChatMessage
{
- /** @var string */
- public $username;
+ public string $username;
- /** @var string */
- public $channel;
+ public string $channel;
- /** @var string */
- public $message;
+ public string $message;
/** @var Emote[] */
- public $possibleEmotes;
+ public array $possibleEmotes;
- public function __construct(string $channel, string $username, string $message)
+ public ?\Illuminate\Foundation\Auth\User $channelUser;
+
+ public ?BotConnection $bot;
+
+ public function __construct(string $channel, string $username, string $message, ?BotConnection $bot = null)
{
$this->channel = $channel;
$this->username = trim($username);
$this->message = trim($message);
+
+ $this->bot = $bot;
+ if ($this->bot) {
+ $this->channelUser = $this->bot->users()->where('name', $this->channel)->first();
+ }
}
- public static function parseIRCMessage(string $message): ?ChatMessage
+ public static function parseIRCMessage(BotConnection $bot, string $message): ?ChatMessage
{
try {
preg_match("/:(.*)\!.*#(\S+) :(.*)/", $message, $matches);
- return new ChatMessage($matches[2], $matches[1], $matches[3]);
+ return new ChatMessage($matches[2], $matches[1], $matches[3], $bot);
} catch (\Exception $exception) {
- echo $exception->getMessage() . "\r\n";
+ Log::error($exception);
}
return null;
@@ -44,16 +52,17 @@ public function toHtml(string $emoteSize = Emote::IMAGE_SIZE_MD): string
$emoteList = collect($this->possibleEmotes)
->map(function (Emote $emote) use ($emoteSize) {
$name = htmlspecialchars_decode($emote->name);
- $regex = '/'.preg_quote($name, '/') . '(\s|$)/';
+ $regex = '/'.preg_quote($name, '/').'(\s|$)/';
if (@preg_match($regex, null) === false) {
- echo "Emote Regex '" . $regex . "' is invalid \r\n";
+ echo "Emote Regex '".$regex."' is invalid \r\n";
+
return null;
}
return [
'name' => $regex,
- 'image' => '
',
+ 'image' => '
',
];
});
diff --git a/src/ChatBot/Twitch/ConnectionHandler.php b/src/ChatBot/Twitch/ConnectionHandler.php
index cced429..6a97e79 100644
--- a/src/ChatBot/Twitch/ConnectionHandler.php
+++ b/src/ChatBot/Twitch/ConnectionHandler.php
@@ -1,15 +1,12 @@
connection = $connection;
- $this->connection->on('message', function ($message) use ($connection) {
+ $this->connection->on('message', function ($message) {
$this->basicMessageHandler($message);
});
}
@@ -62,48 +55,49 @@ public static function withPrivateMessageHandler(WebSocket $connection): Connect
public function privateMessageHandler(string $message): void
{
// if is chat message
- if (strpos($message, 'PRIVMSG') !== false) {
+ if (str_contains($message, 'PRIVMSG')) {
$this->chatMessageReceived($message);
}
}
public function basicMessageHandler(string $message): void
{
- // ignore for basic handler
- if (strpos($message, 'PRIVMSG') !== false) {
+ // if is chat message starts with PRIVMSG ignore basic handler
+ if (str_contains($message, 'PRIVMSG')) {
return;
}
- // get join message
- if (strpos($message, 'NOTICE * :Login authentication failed') !== false) {
- $this->write("LOGIN | " . $message);
+ // if this message contains "Login authentication" reset bot connection
+ if (str_contains($message, 'NOTICE * :Login authentication failed')) {
+ $this->write('LOGIN | '.$message);
event(new BotTokenExpires($this->bot));
$this->connection->close();
+
return;
}
- // get join message
- if (strpos($message, 'PING') !== false) {
+ // handle ping message from twitch
+ if (str_contains($message, 'PING')) {
$this->pingReceived($message);
return;
}
- // get join message
- if (strpos($message, 'JOIN') !== false) {
+ // handle join confirmation
+ if (str_contains($message, 'JOIN')) {
$this->joinMessageReceived($message);
return;
}
- $this->write("UNKOWN | " . $message . PHP_EOL, '');
+ $this->write('UNKOWN | '.$message.PHP_EOL, '');
}
public function pingReceived(string $message): void
{
$this->send('PONG :tmi.twitch.tv');
- $this->write("PING PONG done");
+ $this->write('PING PONG done');
}
public function joinMessageReceived(string $message): void
@@ -111,7 +105,7 @@ public function joinMessageReceived(string $message): void
try {
preg_match("/:(.*)\!.*#(.*)/", $message, $matches);
- $this->write("BOT (" . $matches[1] . ") joined " . $matches[2]);
+ $this->write('BOT ('.$matches[1].') joined '.$matches[2]);
$channelName = trim(strtolower($matches[2]));
@@ -119,9 +113,8 @@ public function joinMessageReceived(string $message): void
$this->runChannelQueue($channelName);
$this->afterJoinCallBacks($channelName);
-
} catch (\Exception $exception) {
- $this->write($exception->getMessage() . ' ' . $exception->getLine() . PHP_EOL, 'ERROR');
+ $this->write($exception->getMessage().' '.$exception->getLine().PHP_EOL, 'ERROR');
}
}
@@ -130,10 +123,8 @@ private function afterJoinCallBacks(string $channelName)
$channelName = strtolower($channelName);
if (isset($this->joinedCallBack[$channelName])) {
-
- $this->write('CALL CALLBACK FOR ' . $channelName);
+ $this->write('CALL CALLBACK FOR '.$channelName);
$this->joinedCallBack[$channelName]();
-
}
}
@@ -142,7 +133,7 @@ public function addJoinedCallBack(string $channelName, callable $callback): void
$channelName = strtolower($channelName);
$this->joinedCallBack[$channelName] = $callback;
- $this->write('Callback added for ' . $channelName);
+ $this->write('Callback added for '.$channelName);
// channel already joined
if (in_array($channelName, $this->joinedChannel)) {
@@ -152,7 +143,7 @@ public function addJoinedCallBack(string $channelName, callable $callback): void
public function chatMessageReceived(string $message): void
{
- $model = ChatMessage::parseIRCMessage($message);
+ $model = ChatMessage::parseIRCMessage($this->bot, $message);
if ($model === null) {
return;
@@ -160,24 +151,13 @@ public function chatMessageReceived(string $message): void
$model->possibleEmotes = $this->emoteSets[$model->channel] ?? [];
- $this->write($model->channel . ' | ' . $model->username . ': ' . $model->message, 'Twitch');
-
- try {
- // Check commands
- foreach ($this->customCommands as $commandHandler) {
- $commandHandler->handle($model);
- }
- } catch (\Exception $exception) {
- $this->write($exception->getMessage(), 'ERROR');
- $this->write($exception->getFile() . ' #' . $exception->getLine(), 'ERROR');
- }
-
- $this->write($model->channel . ' | ' . $model->username . ': ' . $model->message . ' HANDLED');
+ $this->write($model->channel.' | '.$model->username.': '.$model->message, 'Twitch');
try {
event(new ChatMessageReceived($model));
} catch (\Exception $exception) {
- $this->write(" -> EVENT ERROR: " . $exception->getMessage(), 'ERROR');
+ Log::error($exception);
+ $this->write(' -> EVENT ERROR: '.$exception->getMessage(), 'ERROR');
}
}
@@ -185,8 +165,8 @@ public function auth(BotConnection $bot)
{
$this->bot = $bot;
- $this->send('PASS oauth:' . $this->bot->service_token);
- $this->send('NICK ' . strtolower($this->bot->bot_username));
+ $this->send('PASS oauth:'.$this->bot->service_token);
+ $this->send('NICK '.strtolower($this->bot->bot_username));
}
public function send(string $message): void
@@ -194,7 +174,6 @@ public function send(string $message): void
$this->connection->send($message);
}
-
public function joinChannel(Connection $channel): void
{
$channelName = strtolower($channel->service_username);
@@ -202,8 +181,8 @@ public function joinChannel(Connection $channel): void
$this->channelQueue[$channelName] = [];
$this->loadEmotes($channel);
- $this->send('JOIN #' . strtolower($channelName));
- $this->write('JOIN #' . strtolower($channelName));
+ $this->send('JOIN #'.strtolower($channelName));
+ $this->write('JOIN #'.strtolower($channelName));
}
private function loadEmotes(Connection $channel)
@@ -221,7 +200,7 @@ private function runChannelQueue(string $channelName): void
{
$channelName = trim(strtolower($channelName));
- if (!empty($this->channelQueue[$channelName])) {
+ if (! empty($this->channelQueue[$channelName])) {
foreach ($this->channelQueue[$channelName] as $item) {
$this->send($item);
}
@@ -233,10 +212,10 @@ private function runChannelQueue(string $channelName): void
public function sendChatMessage(string $channelName, string $message): void
{
$lowerChannelName = strtolower($channelName);
- $message = 'PRIVMSG #' . $lowerChannelName . ' :' . $message . PHP_EOL;
+ $message = 'PRIVMSG #'.$lowerChannelName.' :'.$message.PHP_EOL;
// send message after channel joined
- if (!in_array($lowerChannelName, $this->joinedChannel)) {
+ if (! in_array($lowerChannelName, $this->joinedChannel)) {
$this->channelQueue[$lowerChannelName][] = $message;
return;
@@ -246,23 +225,9 @@ public function sendChatMessage(string $channelName, string $message): void
$this->write($message);
}
- public function initCustomCommands(): void
- {
- /** @var BotCommand[] $commandClasses */
- $commandClasses = config('openoverlay.bot.commands.advanced');
-
- // add simple command handler
- $commandClasses[] = SimpleBotCommands::class;
-
- foreach ($commandClasses as $commandClass) {
- $this->customCommands[] = new $commandClass($this);
- }
- }
-
protected function write(string $output, string $title = 'OpenOverlay', $newLine = true)
{
- $title = !empty($title) ? '[' . $title . ']' : '';
- echo trim($title . ' ' . $output) . ($newLine ? PHP_EOL : '');
+ $title = ! empty($title) ? '['.$title.']' : '';
+ echo trim($title.' '.$output).($newLine ? PHP_EOL : '');
}
-
}
diff --git a/src/Console/Commands/BroadcastFaker/ChannelCheerFake.php b/src/Console/Commands/BroadcastFaker/ChannelCheerFake.php
index bee8ec6..c7c2913 100644
--- a/src/Console/Commands/BroadcastFaker/ChannelCheerFake.php
+++ b/src/Console/Commands/BroadcastFaker/ChannelCheerFake.php
@@ -4,24 +4,23 @@
class ChannelCheerFake extends Fake
{
-
protected $eventData = [
- "is_anonymous" => false,
- "user_id" => "1234",
- "user_login" => null,
- "user_name" => null,
- "broadcaster_user_id" => "1337",
- "broadcaster_user_login" => "cooler_user",
- "broadcaster_user_name" => "Cooler_User",
- "message" => "This is a bit cheer for you!",
- "bits" => 1000,
+ 'is_anonymous' => false,
+ 'user_id' => '1234',
+ 'user_login' => null,
+ 'user_name' => null,
+ 'broadcaster_user_id' => '1337',
+ 'broadcaster_user_login' => 'cooler_user',
+ 'broadcaster_user_name' => 'Cooler_User',
+ 'message' => 'This is a bit cheer for you!',
+ 'bits' => 1000,
];
protected function randomizeEventData(): array
{
$array = parent::randomizeEventData();
- $anonymous = (bool)random_int(0, 1);
+ $anonymous = (bool) random_int(0, 1);
if ($anonymous === false) {
$username = Fake::fakeUsername();
diff --git a/src/Console/Commands/BroadcastFaker/ChannelFollowFake.php b/src/Console/Commands/BroadcastFaker/ChannelFollowFake.php
index 1250e84..b4b6467 100644
--- a/src/Console/Commands/BroadcastFaker/ChannelFollowFake.php
+++ b/src/Console/Commands/BroadcastFaker/ChannelFollowFake.php
@@ -2,14 +2,13 @@
namespace Redbeed\OpenOverlay\Console\Commands\BroadcastFaker;
-class ChannelFollowFake extends Fake
+class ChannelFollowFake extends Fake
{
-
protected $eventData = [
- "user_id" => "1234",
- "user_name" => "cool_user",
- "broadcaster_user_id" => "1337",
- "broadcaster_user_name" => "cooler_user",
+ 'user_id' => '1234',
+ 'user_name' => 'cool_user',
+ 'broadcaster_user_id' => '1337',
+ 'broadcaster_user_name' => 'cooler_user',
];
protected function randomizeEventData(): array
diff --git a/src/Console/Commands/BroadcastFaker/ChannelRaidFake.php b/src/Console/Commands/BroadcastFaker/ChannelRaidFake.php
index c2a8149..86b5cac 100644
--- a/src/Console/Commands/BroadcastFaker/ChannelRaidFake.php
+++ b/src/Console/Commands/BroadcastFaker/ChannelRaidFake.php
@@ -4,15 +4,14 @@
class ChannelRaidFake extends Fake
{
-
protected $eventData = [
- "from_broadcaster_user_id" => "1234",
- "from_broadcaster_user_login" => "cool_user",
- "from_broadcaster_user_name" => "Cool_User",
- "to_broadcaster_user_id" => "1337",
- "to_broadcaster_user_login" => "cooler_user",
- "to_broadcaster_user_name" => "Cooler_User",
- "viewers" => 9001
+ 'from_broadcaster_user_id' => '1234',
+ 'from_broadcaster_user_login' => 'cool_user',
+ 'from_broadcaster_user_name' => 'Cool_User',
+ 'to_broadcaster_user_id' => '1337',
+ 'to_broadcaster_user_login' => 'cooler_user',
+ 'to_broadcaster_user_name' => 'Cooler_User',
+ 'viewers' => 9001,
];
protected function randomizeEventData(): array
diff --git a/src/Console/Commands/BroadcastFaker/ChannelSubscribeFake.php b/src/Console/Commands/BroadcastFaker/ChannelSubscribeFake.php
index 917db2c..8251cae 100644
--- a/src/Console/Commands/BroadcastFaker/ChannelSubscribeFake.php
+++ b/src/Console/Commands/BroadcastFaker/ChannelSubscribeFake.php
@@ -5,12 +5,12 @@
class ChannelSubscribeFake extends Fake
{
protected $eventData = [
- "user_id" => "1234",
- "user_name" => "cool_user",
- "broadcaster_user_id" => "1337",
- "broadcaster_user_name" => "cooler_user",
- "tier" => "1000",
- "is_gift" => false,
+ 'user_id' => '1234',
+ 'user_name' => 'cool_user',
+ 'broadcaster_user_id' => '1337',
+ 'broadcaster_user_name' => 'cooler_user',
+ 'tier' => '1000',
+ 'is_gift' => false,
];
protected function randomizeEventData(): array
diff --git a/src/Console/Commands/BroadcastFaker/ChannelUpdateFake.php b/src/Console/Commands/BroadcastFaker/ChannelUpdateFake.php
index 797340b..183075f 100644
--- a/src/Console/Commands/BroadcastFaker/ChannelUpdateFake.php
+++ b/src/Console/Commands/BroadcastFaker/ChannelUpdateFake.php
@@ -6,15 +6,14 @@
class ChannelUpdateFake extends Fake
{
-
protected $eventData = [
- "user_id" => "1337",
- "user_name" => "open_overlay_user",
- "title" => "Best Stream Ever",
- "language" => "en",
- "category_id" => "21779",
- "category_name" => "Fortnite",
- "is_mature" => false,
+ 'user_id' => '1337',
+ 'user_name' => 'open_overlay_user',
+ 'title' => 'Best Stream Ever',
+ 'language' => 'en',
+ 'category_id' => '21779',
+ 'category_name' => 'Fortnite',
+ 'is_mature' => false,
];
protected function randomizeEventData(): array
@@ -23,7 +22,7 @@ protected function randomizeEventData(): array
$array['title'] = implode(' ', [
$array['title'],
- '('.Carbon::now()->format('H:i:s').')'
+ '('.Carbon::now()->format('H:i:s').')',
]);
$array['user_name'] = Fake::fakeUsername();
diff --git a/src/Console/Commands/BroadcastFaker/Fake.php b/src/Console/Commands/BroadcastFaker/Fake.php
index c92e019..321b6ea 100644
--- a/src/Console/Commands/BroadcastFaker/Fake.php
+++ b/src/Console/Commands/BroadcastFaker/Fake.php
@@ -1,14 +1,11 @@
randomizeEventData();
}
- public static function fakeUsername(): string {
+ public static function fakeUsername(): string
+ {
return Arr::random([
'Chris',
'redbeed',
@@ -34,7 +32,7 @@ public static function fakeUsername(): string {
'Lethinium',
'kekub',
'Laravel_user',
- 'Twitch_user'
+ 'Twitch_user',
]);
}
}
diff --git a/src/Console/Commands/BroadcastFaker/StreamOffline.php b/src/Console/Commands/BroadcastFaker/StreamOffline.php
index 061b0e8..6b98ae2 100644
--- a/src/Console/Commands/BroadcastFaker/StreamOffline.php
+++ b/src/Console/Commands/BroadcastFaker/StreamOffline.php
@@ -5,8 +5,8 @@
class StreamOffline extends Fake
{
protected $eventData = [
- "broadcaster_user_id" => "u1337",
- "broadcaster_user_login" => "cooler_user",
- "broadcaster_user_name" => "Cooler_user",
+ 'broadcaster_user_id' => 'u1337',
+ 'broadcaster_user_login' => 'cooler_user',
+ 'broadcaster_user_name' => 'Cooler_user',
];
}
diff --git a/src/Console/Commands/BroadcastFaker/StreamOnline.php b/src/Console/Commands/BroadcastFaker/StreamOnline.php
index 1f626fd..a1e2316 100644
--- a/src/Console/Commands/BroadcastFaker/StreamOnline.php
+++ b/src/Console/Commands/BroadcastFaker/StreamOnline.php
@@ -7,12 +7,12 @@
class StreamOnline extends Fake
{
protected $eventData = [
- "id" => "1337",
- "broadcaster_user_id" => "u1337",
- "broadcaster_user_login" => "cooler_user",
- "broadcaster_user_name" => "Cooler_user",
- "type" => "live",
- "started_at" => null,
+ 'id' => '1337',
+ 'broadcaster_user_id' => 'u1337',
+ 'broadcaster_user_login' => 'cooler_user',
+ 'broadcaster_user_name' => 'Cooler_user',
+ 'type' => 'live',
+ 'started_at' => null,
];
protected function randomizeEventData(): array
diff --git a/src/Console/Commands/ChatBot/RestartServerCommand.php b/src/Console/Commands/ChatBot/RestartServerCommand.php
index b9e5ae3..543bf2f 100644
--- a/src/Console/Commands/ChatBot/RestartServerCommand.php
+++ b/src/Console/Commands/ChatBot/RestartServerCommand.php
@@ -26,7 +26,6 @@ class RestartServerCommand extends Command
public function handle(): void
{
-
Cache::forever(
StartCommand::RESTART_CACHE_KEY,
$this->currentTime()
diff --git a/src/Console/Commands/ChatBot/RuntimeCommand.php b/src/Console/Commands/ChatBot/RuntimeCommand.php
index 5464b07..50f5269 100644
--- a/src/Console/Commands/ChatBot/RuntimeCommand.php
+++ b/src/Console/Commands/ChatBot/RuntimeCommand.php
@@ -24,7 +24,6 @@ protected function softShutdown()
{
$this->loop->stop();
- echo "Chatbot Service will shutdown." . PHP_EOL;
+ echo 'Chatbot Service will shutdown.'.PHP_EOL;
}
-
}
diff --git a/src/Console/Commands/ChatBot/SendMessageCommand.php b/src/Console/Commands/ChatBot/SendMessageCommand.php
index af51cc5..9e9d8e0 100644
--- a/src/Console/Commands/ChatBot/SendMessageCommand.php
+++ b/src/Console/Commands/ChatBot/SendMessageCommand.php
@@ -2,13 +2,13 @@
namespace Redbeed\OpenOverlay\Console\Commands\ChatBot;
+use function Ratchet\Client\connect;
use Ratchet\Client\WebSocket;
use Redbeed\OpenOverlay\ChatBot\Twitch\ConnectionHandler;
use Redbeed\OpenOverlay\Models\BotConnection;
use Redbeed\OpenOverlay\Models\User\Connection;
use Redbeed\OpenOverlay\Models\User\UserOpenOverlay;
use Redbeed\OpenOverlay\OpenOverlay;
-use function Ratchet\Client\connect;
class SendMessageCommand extends RuntimeCommand
{
@@ -35,11 +35,13 @@ public function handle(): void
if (trim($message) === null) {
$this->error('Message not filled');
+
return;
}
if ($user === null) {
$this->error('User not found');
+
return;
}
@@ -47,6 +49,7 @@ public function handle(): void
if ($bot === null) {
$this->error('Bot not found');
+
return;
}
@@ -59,7 +62,6 @@ public function handle(): void
$twitchUsers = $user->connections()->where('service', 'twitch')->get();
foreach ($twitchUsers as $twitchUser) {
-
$connectionHandler->joinChannel($twitchUser);
$connectionHandler->sendChatMessage($twitchUser->service_username, $message);
@@ -72,7 +74,6 @@ public function handle(): void
});
});
}
-
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
@@ -90,6 +91,7 @@ private function configureMaxRuntime()
private function getUser()
{
$userId = $this->argument('userId');
+
return (OpenOverlay::userModel())::find($userId);
}
diff --git a/src/Console/Commands/ChatBot/StartCommand.php b/src/Console/Commands/ChatBot/StartCommand.php
index 7118b6a..bffa5d4 100644
--- a/src/Console/Commands/ChatBot/StartCommand.php
+++ b/src/Console/Commands/ChatBot/StartCommand.php
@@ -3,14 +3,13 @@
namespace Redbeed\OpenOverlay\Console\Commands\ChatBot;
use Illuminate\Support\Facades\Cache;
+use function Ratchet\Client\connect;
use Ratchet\Client\WebSocket;
use Redbeed\OpenOverlay\ChatBot\Twitch\ConnectionHandler;
use Redbeed\OpenOverlay\Models\BotConnection;
-use function Ratchet\Client\connect;
class StartCommand extends RuntimeCommand
{
-
const RESTART_CACHE_KEY = 'redbeed:open-overlay:chat-bot:restart';
/**
@@ -28,13 +27,10 @@ class StartCommand extends RuntimeCommand
protected $description = 'Chat Bot worker (loop service)';
/**
- *
* Timestamp of the last restart
- *
*/
private int $lastRestart;
-
public function handle(): void
{
$this->configureRestartTimer();
@@ -65,8 +61,6 @@ private function configureChatbot()
foreach ($bot->users as $user) {
$twitchUsers = $user->connections()->where('service', 'twitch')->get();
- $connectionHandler->initCustomCommands();
-
foreach ($twitchUsers as $twitchUser) {
$connectionHandler->joinChannel($twitchUser);
$connectionHandler->sendChatMessage($twitchUser->service_username, 'Hello');
@@ -76,7 +70,6 @@ private function configureChatbot()
$conn->on('close', function ($code = null, $reason = null) {
echo "Connection closed ({$code} - {$reason})";
});
-
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
diff --git a/src/Console/Commands/EventBroadcastFaker.php b/src/Console/Commands/EventBroadcastFaker.php
index 5fd28fa..0be4583 100644
--- a/src/Console/Commands/EventBroadcastFaker.php
+++ b/src/Console/Commands/EventBroadcastFaker.php
@@ -2,14 +2,12 @@
namespace Redbeed\OpenOverlay\Console\Commands;
-use Carbon\Carbon;
-use Illuminate\Database\Eloquent\Model;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelCheerFake;
+use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelFollowFake;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelRaidFake;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelSubscribeFake;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelUpdateFake;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\Fake;
-use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\ChannelFollowFake;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\StreamOffline;
use Redbeed\OpenOverlay\Console\Commands\BroadcastFaker\StreamOnline;
use Redbeed\OpenOverlay\Events\Twitch\EventReceived;
@@ -62,7 +60,7 @@ public function handle(): void
return;
}
- if (!array_key_exists($type, $this->types)) {
+ if (! array_key_exists($type, $this->types)) {
$this->error('Type is not provided');
return;
@@ -77,6 +75,6 @@ public function handle(): void
]);
broadcast(new EventReceived($fakeEvent));
- $this->info('Event ' . $type . ' for ' . $twitchUserId . ' fired');
+ $this->info('Event '.$type.' for '.$twitchUserId.' fired');
}
}
diff --git a/src/Console/Commands/EventSubDeleteCommand.php b/src/Console/Commands/EventSubDeleteCommand.php
index 72088b3..c321b5a 100644
--- a/src/Console/Commands/EventSubDeleteCommand.php
+++ b/src/Console/Commands/EventSubDeleteCommand.php
@@ -88,10 +88,9 @@ public function handle(): void
$this->subscriptionsTable($subscriptions);
}
- $this->info($subscriptionsCount . ' subscriptions matching your options');
+ $this->info($subscriptionsCount.' subscriptions matching your options');
if ($this->confirm('Do you wish to delete them?')) {
-
$deleteProgress = $this->output->createProgressBar($subscriptionsCount);
$deleteProgress->start();
@@ -99,12 +98,10 @@ public function handle(): void
foreach ($subscriptions as $subscription) {
try {
-
$eventSubClient->deleteSubscription($subscription['id']);
$deleted++;
-
} catch (RequestException $exception) {
- $this->error($subscription['id'] . ' could not deleted');
+ $this->error($subscription['id'].' could not deleted');
}
$deleteProgress->advance();
@@ -113,7 +110,7 @@ public function handle(): void
$deleteProgress->finish();
$this->newLine(2);
- $this->info('Total EventSub deleted: ' . $deleted . '/' . $subscriptionsCount);
+ $this->info('Total EventSub deleted: '.$deleted.'/'.$subscriptionsCount);
}
}
diff --git a/src/Console/Commands/EventSubListingCommand.php b/src/Console/Commands/EventSubListingCommand.php
index 5804808..94807b0 100644
--- a/src/Console/Commands/EventSubListingCommand.php
+++ b/src/Console/Commands/EventSubListingCommand.php
@@ -3,7 +3,6 @@
namespace Redbeed\OpenOverlay\Console\Commands;
use Illuminate\Console\Command;
-use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Redbeed\OpenOverlay\Models\Twitch\EventSubscription;
use Redbeed\OpenOverlay\Service\Twitch\EventSubClient;
@@ -34,11 +33,8 @@ public function __construct()
parent::__construct();
}
-
/**
* Execute the console command.
- *
- * @return int
*/
public function handle()
{
diff --git a/src/Console/Commands/Make/MakeBotSchedulingCommand.php b/src/Console/Commands/Make/MakeBotSchedulingCommand.php
index b267ae1..15346b7 100644
--- a/src/Console/Commands/Make/MakeBotSchedulingCommand.php
+++ b/src/Console/Commands/Make/MakeBotSchedulingCommand.php
@@ -3,9 +3,7 @@
namespace Redbeed\OpenOverlay\Console\Commands\Make;
use Illuminate\Console\GeneratorCommand;
-use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputOption;
class MakeBotSchedulingCommand extends GeneratorCommand
{
diff --git a/src/Console/Commands/Make/Stubs/BotCommand.stub b/src/Console/Commands/Make/Stubs/BotCommand.stub
deleted file mode 100644
index 25c91b6..0000000
--- a/src/Console/Commands/Make/Stubs/BotCommand.stub
+++ /dev/null
@@ -1,18 +0,0 @@
-option('force')) {
+ if (! empty($currentSecret) && ! $this->option('force')) {
$this->warn('You already have a secret');
+
return;
}
@@ -44,7 +44,7 @@ private function writeSecretKeyInEnvironmentFile($key): void
if (preg_match($secretKeyPattern, $envFileContent) === 0) {
file_put_contents(
$envFilePath,
- self::ENV_KEY . '=' . $key . PHP_EOL,
+ self::ENV_KEY.'='.$key.PHP_EOL,
FILE_APPEND
);
@@ -57,7 +57,7 @@ private function writeSecretKeyInEnvironmentFile($key): void
$envFilePath,
preg_replace(
$secretKeyPattern,
- self::ENV_KEY . '=' . $key,
+ self::ENV_KEY.'='.$key,
$envFileContent
)
);
@@ -69,13 +69,14 @@ private function showOption(string $key): void
{
if ($this->option('show')) {
$this->info('New Secret Key:');
- $this->info(self::ENV_KEY . '=' . $key);
+ $this->info(self::ENV_KEY.'='.$key);
}
}
protected function keyReplacementPattern(): string
{
- $escaped = preg_quote('=' . env(self::ENV_KEY, ''), '/');
- return "/^" . self::ENV_KEY . "{$escaped}/m";
+ $escaped = preg_quote('='.config('openoverlay.webhook.twitch.secret'), '/');
+
+ return '/^'.self::ENV_KEY."{$escaped}/m";
}
}
diff --git a/src/Console/Commands/Twitch/OnlineStatusCommand.php b/src/Console/Commands/Twitch/OnlineStatusCommand.php
new file mode 100644
index 0000000..acfcb54
--- /dev/null
+++ b/src/Console/Commands/Twitch/OnlineStatusCommand.php
@@ -0,0 +1,46 @@
+option('all') && $this->argument('twitchUserId')) {
+ $connections = $connections->where('service_user_id', $this->argument('twitchUserId'));
+ }
+
+ $connections = $connections->get();
+ $streamsResponse = (new StreamsClient())->byUserIds($connections->pluck('service_user_id')->toArray());
+ $streamsData = collect($streamsResponse['data'] ?? []);
+
+ foreach ($connections as $connection) {
+ $stream = $streamsData->firstWhere('user_id', $connection->service_user_id);
+
+ if ($stream !== null) {
+ StreamerOnline::setOnline(
+ $connection->service_user_id,
+ Carbon::parse($stream['created_at'] ?? null, 'UTC')
+ );
+
+ $this->info('Streamer '.$connection->service_username.' is online');
+ continue;
+ }
+
+ $this->info('Streamer '.$connection->service_username.' is offline');
+ StreamerOnline::setOffline($connection->service_user_id);
+ }
+ }
+}
diff --git a/src/Console/Commands/Twitch/RefresherCommand.php b/src/Console/Commands/Twitch/RefresherCommand.php
index 57eeba6..73fe88c 100644
--- a/src/Console/Commands/Twitch/RefresherCommand.php
+++ b/src/Console/Commands/Twitch/RefresherCommand.php
@@ -8,7 +8,6 @@
class RefresherCommand extends Command
{
-
protected $signature = 'overlay:twitch:refresher';
protected $description = 'Refresh followers and subscriber for all twitch connections';
@@ -18,7 +17,7 @@ public function handle(): void
$connections = Connection::where('service', 'twitch')->get();
foreach ($connections as $connection) {
- $this->info('Start for ' . $connection->service_username . ' (' . $connection->service_user_id . ')');
+ $this->info('Start for '.$connection->service_username.' ('.$connection->service_user_id.')');
event(new RefresherEvent($connection));
}
diff --git a/src/Console/ConsoleServiceProvider.php b/src/Console/ConsoleServiceProvider.php
index abfcc21..901e9d3 100644
--- a/src/Console/ConsoleServiceProvider.php
+++ b/src/Console/ConsoleServiceProvider.php
@@ -4,19 +4,17 @@
use Illuminate\Support\ServiceProvider;
use Redbeed\OpenOverlay\Console\Commands\ChatBot\RestartServerCommand;
-use Redbeed\OpenOverlay\Console\Commands\ChatBot\StartCommand;
use Redbeed\OpenOverlay\Console\Commands\ChatBot\SendMessageCommand;
+use Redbeed\OpenOverlay\Console\Commands\ChatBot\StartCommand;
use Redbeed\OpenOverlay\Console\Commands\EventBroadcastFaker;
use Redbeed\OpenOverlay\Console\Commands\EventSubDeleteCommand;
use Redbeed\OpenOverlay\Console\Commands\EventSubListingCommand;
-use Redbeed\OpenOverlay\Console\Commands\Make\MakeBotCommandCommand;
-use Redbeed\OpenOverlay\Console\Commands\Make\MakeBotSchedulingCommand;
use Redbeed\OpenOverlay\Console\Commands\SecretCommand;
+use Redbeed\OpenOverlay\Console\Commands\Twitch\OnlineStatusCommand;
use Redbeed\OpenOverlay\Console\Commands\Twitch\RefresherCommand;
class ConsoleServiceProvider extends ServiceProvider
{
-
public function boot(): void
{
$this->registerGlobalCommands();
@@ -36,10 +34,8 @@ protected function registerConsoleCommands(): void
StartCommand::class,
RestartServerCommand::class,
- MakeBotCommandCommand::class,
- MakeBotSchedulingCommand::class,
-
RefresherCommand::class,
+ OnlineStatusCommand::class,
]);
}
diff --git a/src/Console/Scheduling/ChatBotScheduling.php b/src/Console/Scheduling/ChatBotScheduling.php
index 5f6405a..261665b 100644
--- a/src/Console/Scheduling/ChatBotScheduling.php
+++ b/src/Console/Scheduling/ChatBotScheduling.php
@@ -35,11 +35,10 @@ protected function schedule(Event $event): Event
public function getJob(Schedule $schedule, $user): ?Event
{
- if (!$this->valid($user)) {
+ if (! $this->valid($user)) {
return null;
}
return $this->schedule($schedule->command(SendMessageCommand::class, [$user->id, $this->message()]));
}
-
}
diff --git a/src/EventServiceProvider.php b/src/EventServiceProvider.php
index 9e636fc..99d3af5 100644
--- a/src/EventServiceProvider.php
+++ b/src/EventServiceProvider.php
@@ -2,18 +2,21 @@
namespace Redbeed\OpenOverlay;
-use \Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
+use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
+use Illuminate\Support\Facades\Event;
+use Redbeed\OpenOverlay\Automations\Triggers\TwitchChatMessageTrigger;
use Redbeed\OpenOverlay\Events\Twitch\BotTokenExpires;
+use Redbeed\OpenOverlay\Events\Twitch\ChatMessageReceived;
use Redbeed\OpenOverlay\Events\Twitch\EventReceived;
use Redbeed\OpenOverlay\Events\Twitch\RefresherEvent;
use Redbeed\OpenOverlay\Events\UserConnectionChanged;
+use Redbeed\OpenOverlay\Listeners\AutoShoutOutRaid;
use Redbeed\OpenOverlay\Listeners\Twitch\NewFollowerListener;
use Redbeed\OpenOverlay\Listeners\Twitch\NewSubscriberListener;
-use Redbeed\OpenOverlay\Listeners\AutoShoutOutRaid;
+use Redbeed\OpenOverlay\Listeners\Twitch\Refresher\NewConnectionRefresher;
use Redbeed\OpenOverlay\Listeners\Twitch\Refresher\StandardRefresher;
-use Redbeed\OpenOverlay\Listeners\TwitchSplitReceivedEvents;
use Redbeed\OpenOverlay\Listeners\Twitch\UpdateBotToken;
-use Redbeed\OpenOverlay\Listeners\Twitch\Refresher\NewConnectionRefresher;
+use Redbeed\OpenOverlay\Listeners\TwitchSplitReceivedEvents;
use Redbeed\OpenOverlay\Listeners\UpdateUserWebhookCalls;
use Redbeed\OpenOverlay\Sociallite\TwitchClientCredentialsExtendSocialite;
use SocialiteProviders\Manager\SocialiteWasCalled;
@@ -21,15 +24,14 @@
class EventServiceProvider extends ServiceProvider
{
-
protected $listen = [
SocialiteWasCalled::class => [
TwitchExtendSocialite::class,
TwitchClientCredentialsExtendSocialite::class,
],
- BotTokenExpires::class => [
- UpdateBotToken::class
- ]
+ BotTokenExpires::class => [
+ UpdateBotToken::class,
+ ],
];
public function listens(): array
@@ -53,6 +55,10 @@ public function listens(): array
$listen[EventReceived::class][] = NewSubscriberListener::class;
}
+ Event::listen(function (ChatMessageReceived $event) {
+ automation(new TwitchChatMessageTrigger($event->message));
+ });
+
return $listen;
}
diff --git a/src/Events/Twitch/ChatMessageReceived.php b/src/Events/Twitch/ChatMessageReceived.php
index f3169de..70a3ce0 100644
--- a/src/Events/Twitch/ChatMessageReceived.php
+++ b/src/Events/Twitch/ChatMessageReceived.php
@@ -2,6 +2,7 @@
namespace Redbeed\OpenOverlay\Events\Twitch;
+use function config;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
@@ -11,7 +12,6 @@
use Redbeed\OpenOverlay\Models\Twitch\Emote;
use Redbeed\OpenOverlay\Models\User\Connection;
use Redbeed\OpenOverlay\Support\ViewerInChat;
-use function config;
class ChatMessageReceived implements ShouldBroadcastNow
{
@@ -44,7 +44,7 @@ public function viewerInChatListener()
public function broadcastOn(): Channel
{
- return new Channel('twitch.' . $this->twitchUser->service_user_id);
+ return new Channel('twitch.'.$this->twitchUser->service_user_id);
}
public function broadcastAs(): string
diff --git a/src/Events/Twitch/StreamOffline.php b/src/Events/Twitch/StreamOffline.php
index 243cd0d..97e69ce 100644
--- a/src/Events/Twitch/StreamOffline.php
+++ b/src/Events/Twitch/StreamOffline.php
@@ -35,7 +35,7 @@ public function __construct(EventSubEvents $twitchEvent)
public function broadcastOn(): Channel
{
- return new Channel('twitch.' . $this->twitchUser->service_user_id);
+ return new Channel('twitch.'.$this->twitchUser->service_user_id);
}
public function broadcastAs(): string
@@ -47,7 +47,7 @@ public function broadcastWith()
{
return [
'started' => $this->streamStarted,
- 'ended' => Carbon::now(),
+ 'ended' => Carbon::now(),
];
}
}
diff --git a/src/Events/Twitch/StreamOnline.php b/src/Events/Twitch/StreamOnline.php
index 79651a8..d97c446 100644
--- a/src/Events/Twitch/StreamOnline.php
+++ b/src/Events/Twitch/StreamOnline.php
@@ -31,7 +31,7 @@ public function __construct(EventSubEvents $twitchEvent)
public function broadcastOn(): Channel
{
- return new Channel('twitch.' . $this->twitchUser->service_user_id);
+ return new Channel('twitch.'.$this->twitchUser->service_user_id);
}
public function broadcastAs(): string
diff --git a/src/Events/ViewerEnteredChat.php b/src/Events/ViewerEnteredChat.php
index 7eea08d..9d26ce8 100644
--- a/src/Events/ViewerEnteredChat.php
+++ b/src/Events/ViewerEnteredChat.php
@@ -34,6 +34,6 @@ public function broadcastWith()
public function broadcastOn()
{
- return new Channel('twitch.' . $this->streamer->service_user_id);
+ return new Channel('twitch.'.$this->streamer->service_user_id);
}
}
diff --git a/src/Exceptions/AutomationFilterNotValid.php b/src/Exceptions/AutomationFilterNotValid.php
new file mode 100644
index 0000000..bc15c20
--- /dev/null
+++ b/src/Exceptions/AutomationFilterNotValid.php
@@ -0,0 +1,7 @@
+get('event');
- if (!empty($event) && in_array($messageType, config('openoverlay.webhook.twitch.subscribe'), true)) {
-
+ if (! empty($event) && in_array($messageType, config('openoverlay.webhook.twitch.subscribe'), true)) {
return $this->receiveNotification(
$messageId,
$messageType,
$messageTimestamp,
$event
);
-
}
return $request->get('challenge');
@@ -62,6 +60,5 @@ private function receiveNotification(string $eventId, string $eventType, string
}
return \response('Event received', $newEvent->wasRecentlyCreated ? Response::HTTP_CREATED : Response::HTTP_OK);
-
}
}
diff --git a/src/Http/Controllers/Connection/AppTokenController.php b/src/Http/Controllers/Connection/AppTokenController.php
index aacc623..0866f4c 100644
--- a/src/Http/Controllers/Connection/AppTokenController.php
+++ b/src/Http/Controllers/Connection/AppTokenController.php
@@ -4,7 +4,6 @@
class AppTokenController extends SocialiteController
{
-
protected $socialiteDriver = 'twitch_client_credentials';
public function __construct()
diff --git a/src/Http/Controllers/Connection/AuthController.php b/src/Http/Controllers/Connection/AuthController.php
index 3da4a4a..c2c5c55 100644
--- a/src/Http/Controllers/Connection/AuthController.php
+++ b/src/Http/Controllers/Connection/AuthController.php
@@ -6,11 +6,9 @@
use Illuminate\Support\Facades\Auth;
use Redbeed\OpenOverlay\Events\UserConnectionChanged;
use Redbeed\OpenOverlay\Models\User\Connection;
-use Redbeed\OpenOverlay\Service\Twitch\UsersClient;
class AuthController extends SocialiteController
{
-
protected function callbackUrl(): string
{
return route('open_overlay.connection.callback');
diff --git a/src/Http/Controllers/Connection/SocialiteController.php b/src/Http/Controllers/Connection/SocialiteController.php
index 4f03bc0..9c29c8d 100644
--- a/src/Http/Controllers/Connection/SocialiteController.php
+++ b/src/Http/Controllers/Connection/SocialiteController.php
@@ -1,6 +1,5 @@
setScopes($this->scopes());
}
- protected function scopes(): array {
+ protected function scopes(): array
+ {
return config('openoverlay.service.twitch.scopes');
}
@@ -26,12 +26,11 @@ public function redirect(): RedirectResponse
{
$callbackUrl = $this->callbackUrl();
- if (!empty($callbackUrl)) {
+ if (! empty($callbackUrl)) {
/** @var RedirectResponse $redirect */
$redirect = $this->socialite()->redirect();
-
$redirectUrl = Url::fromString($redirect->getTargetUrl());
$redirectUrl = $redirectUrl->withQueryParameter('redirect_uri', $callbackUrl);
diff --git a/src/Listeners/AutoShoutOutRaid.php b/src/Listeners/AutoShoutOutRaid.php
index 3274c34..9cc5427 100644
--- a/src/Listeners/AutoShoutOutRaid.php
+++ b/src/Listeners/AutoShoutOutRaid.php
@@ -23,12 +23,12 @@ public function handle(EventReceived $event)
$connection = Connection::where('service_user_id', $event->event->event_user_id)
->first();
- if (!$connection) {
+ if (! $connection) {
return;
}
$chatMessage = config(
- 'openoverlay.modules' . AutoShoutOutRaid::class . 'message',
+ 'openoverlay.modules'.AutoShoutOutRaid::class.'message',
'Follow :username over at :twitchUrl. They were last playing :gameName'
);
@@ -40,7 +40,7 @@ public function handle(EventReceived $event)
$channels = $channelClient->get($eventData['from_broadcaster_user_id']);
$channel = head($channels['data']);
- if (!empty($channel['game_id'])) {
+ if (! empty($channel['game_id'])) {
$gameName = $channel['game_name'];
}
} catch (ClientException $exception) {
@@ -52,7 +52,7 @@ public function handle(EventReceived $event)
'userId' => $connection->user->id,
'message' => __($chatMessage, [
'username' => $eventData['from_broadcaster_user_name'],
- 'twitchUrl' => 'https://www.twitch.tv/' . $eventData['from_broadcaster_user_login'],
+ 'twitchUrl' => 'https://www.twitch.tv/'.$eventData['from_broadcaster_user_login'],
'gameName' => $gameName,
]),
]);
diff --git a/src/Listeners/Twitch/EventListener.php b/src/Listeners/Twitch/EventListener.php
index 5667ea4..6512454 100644
--- a/src/Listeners/Twitch/EventListener.php
+++ b/src/Listeners/Twitch/EventListener.php
@@ -7,7 +7,6 @@
abstract class EventListener implements ShouldQueue
{
-
protected function eventType(): string
{
return 'event.type'; // https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types
@@ -27,6 +26,5 @@ public function handle(EventReceived $event)
$this->handleEvent($event);
}
-
abstract public function handleEvent(EventReceived $event): void;
}
diff --git a/src/Listeners/Twitch/NewFollowerListener.php b/src/Listeners/Twitch/NewFollowerListener.php
index d5aefba..7d698f1 100644
--- a/src/Listeners/Twitch/NewFollowerListener.php
+++ b/src/Listeners/Twitch/NewFollowerListener.php
@@ -5,11 +5,9 @@
use Carbon\Carbon;
use Redbeed\OpenOverlay\Events\Twitch\EventReceived;
use Redbeed\OpenOverlay\Models\Twitch\UserFollowers;
-use Redbeed\OpenOverlay\Models\Twitch\UserSubscriber;
class NewFollowerListener extends EventListener
{
-
protected function eventType(): string
{
return 'channel.follow';
@@ -26,11 +24,11 @@ public function handleEvent(EventReceived $event): void
if (empty($followerModal)) {
UserFollowers::create([
- 'twitch_user_id' => $event->event->event_user_id,
- 'follower_user_id' => $followerData['user_id'],
+ 'twitch_user_id' => $event->event->event_user_id,
+ 'follower_user_id' => $followerData['user_id'],
'follower_username' => $followerData['user_name'],
- 'followed_at' => Carbon::parse($followerData['followed_at']),
- 'deleted_at' => null,
+ 'followed_at' => Carbon::parse($followerData['followed_at']),
+ 'deleted_at' => null,
]);
return;
diff --git a/src/Listeners/Twitch/NewSubscriberListener.php b/src/Listeners/Twitch/NewSubscriberListener.php
index c05ea9c..7586b1e 100644
--- a/src/Listeners/Twitch/NewSubscriberListener.php
+++ b/src/Listeners/Twitch/NewSubscriberListener.php
@@ -17,13 +17,13 @@ public function handleEvent(EventReceived $event): void
$subscriberData = $event->event->event_data;
UserSubscriber::firstOrCreate([
- 'twitch_user_id' => $event->event->event_user_id,
+ 'twitch_user_id' => $event->event->event_user_id,
'subscriber_user_id' => $subscriberData['user_id'],
], [
'subscriber_username' => $subscriberData['user_name'],
- 'tier' => $subscriberData['user_name'],
- 'tier_name' => $subscriberData['plan_name'],
- 'is_gift' => $subscriberData['is_gift'],
+ 'tier' => $subscriberData['user_name'],
+ 'tier_name' => $subscriberData['plan_name'] ?? '',
+ 'is_gift' => $subscriberData['is_gift'],
]);
}
}
diff --git a/src/Listeners/Twitch/Refresher/LoginRefresher.php b/src/Listeners/Twitch/Refresher/LoginRefresher.php
index 88a9efb..c0d8e44 100644
--- a/src/Listeners/Twitch/Refresher/LoginRefresher.php
+++ b/src/Listeners/Twitch/Refresher/LoginRefresher.php
@@ -2,6 +2,7 @@
namespace Redbeed\OpenOverlay\Listeners\Twitch\Refresher;
+use Exception;
use Illuminate\Auth\Events\Login;
use Illuminate\Contracts\Queue\ShouldQueue;
use Redbeed\OpenOverlay\Exceptions\WrongConnectionTypeException;
@@ -27,7 +28,13 @@ public function handle(Login $event)
}
if (parent::saveSubscriber()) {
- $this->refreshSubscriber($twitchConnection);
+ try {
+ $this->refreshSubscriber($twitchConnection);
+ } catch (Exception $e) {
+ // ignore exception as it is not critical
+ // user auth token is not valid
+ report($e);
+ }
}
}
}
diff --git a/src/Listeners/Twitch/Refresher/Refresher.php b/src/Listeners/Twitch/Refresher/Refresher.php
index 4e122bc..3ad0d88 100644
--- a/src/Listeners/Twitch/Refresher/Refresher.php
+++ b/src/Listeners/Twitch/Refresher/Refresher.php
@@ -3,6 +3,7 @@
namespace Redbeed\OpenOverlay\Listeners\Twitch\Refresher;
use Carbon\Carbon;
+use function head;
use Illuminate\Support\Arr;
use Redbeed\OpenOverlay\Exceptions\WrongConnectionTypeException;
use Redbeed\OpenOverlay\Models\Twitch\UserFollowers;
@@ -10,7 +11,6 @@
use Redbeed\OpenOverlay\Models\User\Connection;
use Redbeed\OpenOverlay\Service\Twitch\SubscriptionsClient;
use Redbeed\OpenOverlay\Service\Twitch\UsersClient;
-use function head;
abstract class Refresher
{
@@ -19,7 +19,7 @@ public static function saveFollowers(): bool
return config('openoverlay.service.twitch.save.follower', false) === true;
}
- public static function saveSubscriber(): bool
+ public static function saveSubscriber(): bool
{
return config('openoverlay.service.twitch.save.subscriber', false) === true;
}
@@ -49,11 +49,11 @@ protected function refreshFollowers(Connection $twitchConnection)
if ($followerModal === null) {
UserFollowers::create([
- 'twitch_user_id' => $twitchConnection->service_user_id,
- 'follower_user_id' => $followerData['from_id'],
+ 'twitch_user_id' => $twitchConnection->service_user_id,
+ 'follower_user_id' => $followerData['from_id'],
'follower_username' => $followerData['from_name'],
- 'followed_at' => Carbon::parse($followerData['followed_at']),
- 'deleted_at' => null,
+ 'followed_at' => Carbon::parse($followerData['followed_at']),
+ 'deleted_at' => null,
]);
continue;
@@ -107,16 +107,16 @@ protected function refreshSubscriber(Connection $twitchConnection)
UserSubscriber::create([
'twitch_user_id' => $twitchConnection->service_user_id,
- 'subscriber_user_id' => $subscriberData['user_id'],
- 'subscriber_username' => $subscriberData['user_name'],
+ 'subscriber_user_id' => $subscriberData['user_id'],
+ 'subscriber_username' => $subscriberData['user_name'],
'subscriber_login_name' => $subscriberData['user_login'],
- 'tier' => $subscriberData['tier'],
+ 'tier' => $subscriberData['tier'],
'tier_name' => $subscriberData['plan_name'],
- 'is_gift' => $subscriberData['is_gift'],
- 'gifter_user_id' => $subscriberData['gifter_id'],
- 'gifter_username' => $subscriberData['gifter_name'],
+ 'is_gift' => $subscriberData['is_gift'],
+ 'gifter_user_id' => $subscriberData['gifter_id'],
+ 'gifter_username' => $subscriberData['gifter_name'],
'gifter_login_name' => $subscriberData['gifter_login'],
]);
@@ -149,7 +149,7 @@ protected function refreshSubscriber(Connection $twitchConnection)
private function twitchUser(string $broadcasterId): array
{
$userClient = new UsersClient();
+
return head(Arr::get($userClient->byId($broadcasterId), 'data', []));
}
-
}
diff --git a/src/Listeners/Twitch/Refresher/StandardRefresher.php b/src/Listeners/Twitch/Refresher/StandardRefresher.php
index a30efcc..235db64 100644
--- a/src/Listeners/Twitch/Refresher/StandardRefresher.php
+++ b/src/Listeners/Twitch/Refresher/StandardRefresher.php
@@ -2,6 +2,7 @@
namespace Redbeed\OpenOverlay\Listeners\Twitch\Refresher;
+use GuzzleHttp\Exception\ClientException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Redbeed\OpenOverlay\Events\Twitch\RefresherEvent;
use Redbeed\OpenOverlay\Exceptions\WrongConnectionTypeException;
@@ -22,7 +23,13 @@ public function handle(RefresherEvent $event)
}
if (parent::saveSubscriber()) {
- $this->refreshSubscriber($event->twitchConnection);
+ try {
+ $this->refreshSubscriber($event->twitchConnection);
+ } catch (ClientException $e) {
+ // ignore exception as it is not critical
+ // user auth token is not valid
+ report($e);
+ }
}
}
}
diff --git a/src/Listeners/Twitch/UpdateBotToken.php b/src/Listeners/Twitch/UpdateBotToken.php
index d1e269b..d75acbe 100644
--- a/src/Listeners/Twitch/UpdateBotToken.php
+++ b/src/Listeners/Twitch/UpdateBotToken.php
@@ -20,7 +20,7 @@ public function handle(BotTokenExpires $event)
$client = AuthClient::http();
$response = $client->refreshToken($event->botModel->service_refresh_token);
} catch (\Exception $exception) {
- Log::error("Bot Connection deleted");
+ Log::error('Bot Connection deleted');
Log::error($exception);
return;
diff --git a/src/Listeners/TwitchSplitReceivedEvents.php b/src/Listeners/TwitchSplitReceivedEvents.php
index fbb4a46..2543d58 100644
--- a/src/Listeners/TwitchSplitReceivedEvents.php
+++ b/src/Listeners/TwitchSplitReceivedEvents.php
@@ -12,11 +12,13 @@ public function handle(EventReceived $twitchEvent)
{
if ($twitchEvent->event->event_type === 'stream.online') {
broadcast(new StreamOnline($twitchEvent->event));
+
return;
}
if ($twitchEvent->event->event_type === 'stream.offline') {
broadcast(new StreamOffline($twitchEvent->event));
+
return;
}
}
diff --git a/src/Models/BotConnection.php b/src/Models/BotConnection.php
index 1f43a4a..1efa4ea 100644
--- a/src/Models/BotConnection.php
+++ b/src/Models/BotConnection.php
@@ -42,11 +42,13 @@ public function setServiceRefreshTokenAttribute($value): void
$this->attributes['service_refresh_token'] = $this->encryptString($value);
}
- private function decryptString(string $key): string {
+ private function decryptString(string $key): string
+ {
return Crypt::decryptString($key);
}
- private function encryptString(string $value): string {
+ private function encryptString(string $value): string
+ {
return Crypt::encryptString($value);
}
diff --git a/src/Models/Twitch/Emote.php b/src/Models/Twitch/Emote.php
index 0afcf7f..56801ef 100644
--- a/src/Models/Twitch/Emote.php
+++ b/src/Models/Twitch/Emote.php
@@ -5,7 +5,9 @@
class Emote
{
const IMAGE_SIZE_SM = 'url_1x';
+
const IMAGE_SIZE_MD = 'url_2x';
+
const IMAGE_SIZE_LG = 'url_4x';
/** @var string */
@@ -34,15 +36,15 @@ public static function fromJson(array $emoteData): Emote
$emote->name = $emoteData['name'];
$emote->images = $emoteData['images'];
- if (!empty($emoteData['tier'])) {
+ if (! empty($emoteData['tier'])) {
$emote->tier = $emoteData['tier'];
}
- if (!empty($emoteData['emote_type'])) {
+ if (! empty($emoteData['emote_type'])) {
$emote->emoteType = $emoteData['emote_type'];
}
- if (!empty($emoteData['emote_set_id'])) {
+ if (! empty($emoteData['emote_set_id'])) {
$emote->emoteSetId = $emoteData['emote_set_id'];
}
@@ -53,5 +55,4 @@ public function image(string $size = Emote::IMAGE_SIZE_MD): string
{
return $this->images[$size];
}
-
}
diff --git a/src/Models/Twitch/EventSubEvents.php b/src/Models/Twitch/EventSubEvents.php
index c747b40..a4c58e8 100644
--- a/src/Models/Twitch/EventSubEvents.php
+++ b/src/Models/Twitch/EventSubEvents.php
@@ -5,8 +5,6 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Redbeed\OpenOverlay\Database\Factories\EventSubEventsFactory;
-use Redbeed\OpenOverlay\Models\User\Connection;
-use Redbeed\OpenOverlay\OpenOverlay;
class EventSubEvents extends Model
{
diff --git a/src/Models/Twitch/EventSubscription.php b/src/Models/Twitch/EventSubscription.php
index 44faaef..ef263b3 100644
--- a/src/Models/Twitch/EventSubscription.php
+++ b/src/Models/Twitch/EventSubscription.php
@@ -1,9 +1,7 @@
attributes['service_refresh_token'] = $this->encryptString($value);
}
- private function decryptString(string $key): string {
+ private function decryptString(string $key): string
+ {
return Crypt::decryptString($key);
}
- private function encryptString(string $value): string {
+ private function encryptString(string $value): string
+ {
return Crypt::encryptString($value);
}
diff --git a/src/OpenOverlay.php b/src/OpenOverlay.php
index 1d76f05..db72a1e 100644
--- a/src/OpenOverlay.php
+++ b/src/OpenOverlay.php
@@ -2,10 +2,8 @@
namespace Redbeed\OpenOverlay;
-
class OpenOverlay
{
-
public static function userModel(): string
{
return config('auth.providers.users.model');
diff --git a/src/OpenOverlayServiceProvider.php b/src/OpenOverlayServiceProvider.php
index db6b249..dc2b513 100644
--- a/src/OpenOverlayServiceProvider.php
+++ b/src/OpenOverlayServiceProvider.php
@@ -4,6 +4,8 @@
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\ServiceProvider;
+use Redbeed\OpenOverlay\Automations\AutomationsServiceProvider;
+use Redbeed\OpenOverlay\Console\Commands\Twitch\OnlineStatusCommand;
use Redbeed\OpenOverlay\Console\ConsoleServiceProvider;
use Redbeed\OpenOverlay\Console\Scheduling\ChatBotScheduling;
use Redbeed\OpenOverlay\Models\BotConnection;
@@ -17,16 +19,21 @@ class OpenOverlayServiceProvider extends ServiceProvider
*/
public function boot(): void
{
- // $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'redbeed');
- // $this->loadViewsFrom(__DIR__.'/../resources/views', 'redbeed');
-
- $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
- $this->loadRoutesFrom(__DIR__ . '/../routes/openoverlay.php');
+ $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
+ $this->loadRoutesFrom(__DIR__.'/../routes/openoverlay.php');
// Publishing is only necessary when using the CLI.
if ($this->app->runningInConsole()) {
$this->bootForConsole();
}
+
+ // Register schedule
+ $this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
+ // check streamer status every hour
+ $schedule
+ ->command(OnlineStatusCommand::class, ['--all' => true])->hourly()
+ ->withoutOverlapping();
+ });
}
/**
@@ -38,8 +45,9 @@ public function register(): void
{
$this->app->register(EventServiceProvider::class);
$this->app->register(ConsoleServiceProvider::class);
+ $this->app->register(AutomationsServiceProvider::class);
- $this->mergeConfigFrom(__DIR__ . '/../config/openoverlay.php', 'openoverlay');
+ $this->mergeConfigFrom(__DIR__.'/../config/openoverlay.php', 'openoverlay');
// Register the service the package provides.
$this->app->singleton('openoverlay', function ($app) {
@@ -57,7 +65,6 @@ public function provides()
return ['openoverlay'];
}
-
/**
* Console-specific booting.
*
@@ -67,7 +74,7 @@ protected function bootForConsole(): void
{
// Publishing the configuration file.
$this->publishes([
- __DIR__ . '/../config/openoverlay.php' => config_path('openoverlay.php'),
+ __DIR__.'/../config/openoverlay.php' => config_path('openoverlay.php'),
], 'openoverlay.config');
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
@@ -85,9 +92,7 @@ private function registerSchedule(Schedule $schedule): void
foreach ($bots as $bot) {
foreach ($bot->users as $user) {
foreach ($scheduledMessages as $message) {
-
(new $message())->getJob($schedule, $user);
-
}
}
}
diff --git a/src/Service/Twitch/ApiClient.php b/src/Service/Twitch/ApiClient.php
index d217348..e6350de 100644
--- a/src/Service/Twitch/ApiClient.php
+++ b/src/Service/Twitch/ApiClient.php
@@ -26,7 +26,7 @@ public function __construct()
RequestOptions::HEADERS => [
'Client-ID' => $clientId,
'Accept' => 'application/json',
- 'Authorization' => 'Bearer ' . $authCode,
+ 'Authorization' => 'Bearer '.$authCode,
],
]);
}
@@ -41,6 +41,7 @@ public static function http()
/**
* @return static
+ *
* @throws AppTokenMissing
*/
public function addAppToken()
@@ -53,28 +54,26 @@ public function addAppToken()
return $this->withOptions([
RequestOptions::HEADERS => [
- 'Authorization' => 'Bearer ' . $appToken,
+ 'Authorization' => 'Bearer '.$appToken,
],
]);
}
/**
- * @param string $appToken
- *
+ * @param string $appToken
* @return static
*/
public function withAppToken(string $appToken)
{
return $this->setOptions([
RequestOptions::HEADERS => [
- 'Authorization' => 'Bearer ' . $appToken,
+ 'Authorization' => 'Bearer '.$appToken,
],
]);
}
/**
- * @param array $options
- *
+ * @param array $options
* @return static
*/
public function withOptions(array $options)
@@ -86,8 +85,7 @@ public function withOptions(array $options)
}
/**
- * @param array $options
- *
+ * @param array $options
* @return static
*/
public function setOptions(array $options): self
@@ -99,16 +97,16 @@ public function setOptions(array $options): self
}
/**
- * @param string $method
- * @param string $url
- *
+ * @param string $method
+ * @param string $url
* @return array
+ *
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function request(string $method, string $url)
{
$response = $this->httpClient->request($method, $url, $this->options);
- $json = (string)$response->getBody();
+ $json = (string) $response->getBody();
return json_decode($json, true);
}
diff --git a/src/Service/Twitch/ChannelsClient.php b/src/Service/Twitch/ChannelsClient.php
index 2a007b4..ff34f92 100644
--- a/src/Service/Twitch/ChannelsClient.php
+++ b/src/Service/Twitch/ChannelsClient.php
@@ -17,4 +17,12 @@ public function get(string $broadcasterId): array
])
->request('GET', 'channels');
}
+
+ public function lastGame(string $broadcasterId)
+ {
+ $channels = (new self)->get($broadcasterId);
+ $channel = head($channels['data']);
+
+ return $channel['game_name'];
+ }
}
diff --git a/src/Service/Twitch/ChatEmotesClient.php b/src/Service/Twitch/ChatEmotesClient.php
index 02e1947..7187e33 100644
--- a/src/Service/Twitch/ChatEmotesClient.php
+++ b/src/Service/Twitch/ChatEmotesClient.php
@@ -2,20 +2,18 @@
namespace Redbeed\OpenOverlay\Service\Twitch;
-use Exception;
-use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\RequestOptions;
use Redbeed\OpenOverlay\Exceptions\TwitchEmoteSetIdException;
use Redbeed\OpenOverlay\Models\Twitch\Emote;
class ChatEmotesClient extends ApiClient
{
-
const MAX_SET_ID = 25;
/**
- * @param string $broadcasterId
+ * @param string $broadcasterId
* @return Emote[]
+ *
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Redbeed\OpenOverlay\Exceptions\AppTokenMissing
*/
@@ -38,8 +36,9 @@ public function get(string $broadcasterId): array
}
/**
- * @param string $broadcasterId
+ * @param string $broadcasterId
* @return Emote[]
+ *
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Redbeed\OpenOverlay\Exceptions\AppTokenMissing
*/
@@ -57,8 +56,9 @@ public function global(): array
}
/**
- * @param int $setId
+ * @param int $setId
* @return array
+ *
* @throws TwitchEmoteSetIdException
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Redbeed\OpenOverlay\Exceptions\AppTokenMissing
@@ -66,7 +66,7 @@ public function global(): array
public function set(int $setId): array
{
if ($setId > ChatEmotesClient::MAX_SET_ID || $setId < 1) {
- throw new TwitchEmoteSetIdException('Set Id minimum: 1 / maximum: ' . ChatEmotesClient::MAX_SET_ID);
+ throw new TwitchEmoteSetIdException('Set Id minimum: 1 / maximum: '.ChatEmotesClient::MAX_SET_ID);
}
$json = $this
@@ -91,7 +91,6 @@ public function allSets(): array
$bulkSize = 10;
foreach (range(1, (ChatEmotesClient::MAX_SET_ID / $bulkSize)) as $bulk) {
-
$to = min(($bulkSize * $bulk), ChatEmotesClient::MAX_SET_ID);
$from = ($bulkSize * $bulk) - 9;
diff --git a/src/Service/Twitch/DateTime.php b/src/Service/Twitch/DateTime.php
index ed52186..00cf663 100644
--- a/src/Service/Twitch/DateTime.php
+++ b/src/Service/Twitch/DateTime.php
@@ -9,7 +9,7 @@ class DateTime
public static function parse($dateString): Carbon
{
// twitch timestamps sometimes (randomly) to long
- $timestamp = substr(trim($dateString, 'Z'), 0, 23) . 'Z';
+ $timestamp = substr(trim($dateString, 'Z'), 0, 23).'Z';
return Carbon::createFromFormat(\DateTime::RFC3339_EXTENDED, $timestamp);
}
diff --git a/src/Service/Twitch/EventSubClient.php b/src/Service/Twitch/EventSubClient.php
index 42b43e1..efb2a26 100644
--- a/src/Service/Twitch/EventSubClient.php
+++ b/src/Service/Twitch/EventSubClient.php
@@ -16,6 +16,7 @@ class EventSubClient extends ApiClient
/**
* @return EventSubClient
+ *
* @throws AppTokenMissing
*/
public static function http()
@@ -28,7 +29,7 @@ public static function http()
return (new self())->setOptions([
RequestOptions::HEADERS => [
- 'Authorization' => 'Bearer ' . $appToken,
+ 'Authorization' => 'Bearer '.$appToken,
],
]);
}
@@ -38,14 +39,13 @@ public static function verifySignature(
string $messageId,
string $messageTimestamp,
string $requestBody
- ): bool
- {
+ ): bool {
if (empty($messageId) || empty($messageSignature) || empty($messageTimestamp) || empty($requestBody)) {
throw new WebhookTwitchSignatureMissing('Twitch Eventsub Header infomation missing');
}
- $message = $messageId . $messageTimestamp . $requestBody;
- $hash = 'sha256=' . hash_hmac('sha256', $message, config('openoverlay.webhook.twitch.secret'));
+ $message = $messageId.$messageTimestamp.$requestBody;
+ $hash = 'sha256='.hash_hmac('sha256', $message, config('openoverlay.webhook.twitch.secret'));
return $hash === $messageSignature;
}
@@ -70,7 +70,7 @@ public function subscribe(string $type, string $webhookCallback, array $conditio
'condition' => $condition,
'transport' => [
'method' => 'webhook',
- 'callback' => $webhookCallback . '?' . time(),
+ 'callback' => $webhookCallback.'?'.time(),
'secret' => $secret,
],
],
@@ -84,16 +84,15 @@ public function deleteSubByBroadcasterId(string $broadcasterUserId)
->subscriptions()
->filter(function ($subscription) use ($broadcasterUserId) {
/** @var EventSubscription $subscription */
-
if (empty($subscription->condition)) {
return false;
}
- if (!empty($subscription->condition['broadcaster_user_id']) && $subscription->condition['broadcaster_user_id'] !== $broadcasterUserId) {
+ if (! empty($subscription->condition['broadcaster_user_id']) && $subscription->condition['broadcaster_user_id'] !== $broadcasterUserId) {
return false;
}
- if (!empty($subscription->condition['to_broadcaster_user_id']) && $subscription->condition['to_broadcaster_user_id'] !== $broadcasterUserId) {
+ if (! empty($subscription->condition['to_broadcaster_user_id']) && $subscription->condition['to_broadcaster_user_id'] !== $broadcasterUserId) {
return false;
}
diff --git a/src/Service/Twitch/UsersClient.php b/src/Service/Twitch/UsersClient.php
index bcbc944..e2c6ac7 100644
--- a/src/Service/Twitch/UsersClient.php
+++ b/src/Service/Twitch/UsersClient.php
@@ -2,11 +2,17 @@
namespace Redbeed\OpenOverlay\Service\Twitch;
-use GuzzleHttp\Exception\ClientException;
+use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
+use Illuminate\Support\Arr;
+use Redbeed\OpenOverlay\Exceptions\AppTokenMissing;
class UsersClient extends ApiClient
{
+ /**
+ * @throws AppTokenMissing
+ * @throws GuzzleException
+ */
public function byId(string $id): array
{
return $this
@@ -19,6 +25,10 @@ public function byId(string $id): array
->request('GET', 'users');
}
+ /**
+ * @throws AppTokenMissing
+ * @throws GuzzleException
+ */
public function byUsername(string $username): array
{
return $this
@@ -31,6 +41,10 @@ public function byUsername(string $username): array
->request('GET', 'users');
}
+ /**
+ * @throws AppTokenMissing
+ * @throws GuzzleException
+ */
public function followers(string $twitchUserId): array
{
return $this
@@ -43,6 +57,10 @@ public function followers(string $twitchUserId): array
->request('GET', 'users/follows');
}
+ /**
+ * @throws AppTokenMissing
+ * @throws GuzzleException
+ */
public function allFollowers(string $twitchUserId): array
{
$firstResponse = $this
@@ -78,4 +96,23 @@ public function allFollowers(string $twitchUserId): array
return $firstResponse;
}
+
+ /**
+ * @throws AppTokenMissing
+ * @throws GuzzleException
+ */
+ public static function lastGame(string $username): string
+ {
+ $users = (new self)->byUsername($username);
+ if (empty($users['data']) || count($users['data']) === 0) {
+ return '';
+ }
+
+ $user = Arr::first($users['data']);
+ if (empty($user) || empty($user['id'])) {
+ return '';
+ }
+
+ return (new ChannelsClient())->lastGame($user['id']);
+ }
}
diff --git a/src/Sociallite/TwitchClientCredentialsExtendSocialite.php b/src/Sociallite/TwitchClientCredentialsExtendSocialite.php
index 227b3df..2d3d382 100644
--- a/src/Sociallite/TwitchClientCredentialsExtendSocialite.php
+++ b/src/Sociallite/TwitchClientCredentialsExtendSocialite.php
@@ -9,7 +9,7 @@ class TwitchClientCredentialsExtendSocialite
/**
* Register the provider.
*
- * @param \SocialiteProviders\Manager\SocialiteWasCalled $socialiteWasCalled
+ * @param \SocialiteProviders\Manager\SocialiteWasCalled $socialiteWasCalled
*/
public function handle(SocialiteWasCalled $socialiteWasCalled)
{
diff --git a/src/Support/Facades/Automation.php b/src/Support/Facades/Automation.php
new file mode 100644
index 0000000..fe0ed5b
--- /dev/null
+++ b/src/Support/Facades/Automation.php
@@ -0,0 +1,17 @@
+service_user_id . '.viewer.' . StreamerOnline::onlineTime($streamer->service_user_id, $platform);
+ return $platform.'.streamer.'.$streamer->service_user_id.'.viewer.'.StreamerOnline::onlineTime($streamer->service_user_id, $platform);
}
public static function list(Connection $streamer, string $platform = 'twitch'): array
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
new file mode 100644
index 0000000..aed3c3b
--- /dev/null
+++ b/src/Support/helpers.php
@@ -0,0 +1,14 @@
+trigger(...$args);
+ }
+}