Skip to content
7 changes: 6 additions & 1 deletion src/backend/effects/builtin-effect-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ exports.loadEffects = () => {
'create-prediction',
'lock-prediction',
'resolve-prediction',
'update-vip-role'

'update-vip-role',

'pin-chat-message',
'update-pinned-chat-message',
'unpin-chat-message'
].forEach((filename) => {
const definition = require(`../streaming-platforms/twitch/effects/${filename}`);
EffectManager.registerEffect(definition);
Expand Down
65 changes: 64 additions & 1 deletion src/backend/effects/builtin/chat.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import type { EffectType } from "../../../types";
import { LoggerCache } from "../../logger-cache";

import { TwitchApi } from '../../streaming-platforms/twitch/api';

const logger = LoggerCache.getLogger("Effects");

const effect: EffectType<{
chatter: string;
message: string;
me: boolean;
whisper: string;
sendAsReply: boolean;
pin: boolean;
pinUntilEndOfStream: boolean;
pinDuration?: string;
}> = {
definition: {
id: "firebot:chat",
Expand Down Expand Up @@ -63,6 +69,29 @@ const effect: EffectType<{
</div>
</eos-container>

<eos-container header="Pin Message" pad-top="true" ng-hide="effect.whisper">
<div style="display: flex; flex-direction: row; width: 100%; margin: 0 0 10px 0; align-items: center;">
<firebot-checkbox
label="Pin message"
tooltip="Pin message to the top of chat"
model="effect.pin"
style="margin: 0px 15px 0px 0px"
/>
<firebot-checkbox
ng-show="effect.pin === true"
label="Pin until end of stream"
model="effect.pinUntilEndOfStream"
style="margin: 0px 15px 0px 0px"
/>
</div>
<firebot-input
ng-show="effect.pin === true && effect.pinUntilEndOfStream !== true"
model="effect.pinDuration"
input-title="Duration (in secs)"
placeholder-text="Enter duration"
/>
</eos-container>

`,
optionsController: ($scope) => {
$scope.showWhisperInput = $scope.effect.whisper != null && $scope.effect.whisper !== '';
Expand All @@ -72,6 +101,12 @@ const effect: EffectType<{
if (effect.message == null || effect.message === "") {
errors.push("Chat message can't be blank.");
}
if (effect.pin === true
&& effect.pinUntilEndOfStream !== true
&& !effect.pinDuration?.length
) {
errors.push("Must choose pin duration");
}
return errors;
},
onTriggerEvent: async ({ effect, trigger }) => {
Expand All @@ -93,7 +128,35 @@ const effect: EffectType<{
const user = await TwitchApi.users.getUserByName(effect.whisper);
await TwitchApi.whispers.sendWhisper(user.id, effect.message, sendAsBot);
} else {
await TwitchApi.chat.sendChatMessage(effect.message, effect.sendAsReply ? messageId : null, sendAsBot);
const sendResult = await TwitchApi.chat.sendChatMessage(effect.message, effect.sendAsReply ? messageId : null, sendAsBot);

if (effect.pin === true) {
if (sendResult.success === true) {
if (sendResult.isSlashCommand !== true) {
let pinDuration: number = undefined;

if (effect.pinUntilEndOfStream !== true
&& !!effect.pinDuration?.length
) {
pinDuration = Number(effect.pinDuration);

if (isNaN(pinDuration)) {
pinDuration = undefined;
} else if (pinDuration < 30) {
pinDuration = 30;
} else if (pinDuration > 1800) {
pinDuration = 1800;
}
}

await TwitchApi.chat.pinChatMessage(sendResult.messageId, pinDuration);
} else {
logger.warn("Chat message not pinned due to being processed as slash command");
}
} else {
logger.warn("Message failed to send. Unable to pin.");
}
}
}

return true;
Expand Down
7 changes: 7 additions & 0 deletions src/backend/streaming-platforms/twitch/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ class TwitchApi {
: this.streamerClient;
}

get moderatorId(): string {
const modUser = SettingsManager.getSetting("DefaultModerationUser");
return modUser === "bot" && this.accounts.bot.loggedIn === true
? this.accounts.bot.userId
: this.accounts.streamer.userId;
}

get accounts() {
return AccountAccess.getAccounts();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export abstract class ApiResourceBase<T extends ListenerSignature<T> = unknown>
return this._apiBase.moderationClient;
}

protected get moderatorId(): string {
return this._apiBase.moderatorId;
}

protected get logger() {
return this._apiBase.logger;
}
Expand Down
Loading