mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-18 01:50:50 +00:00
feat: Add sending chat command responses as whispers/replies/announcements
This commit is contained in:
parent
f83a6c6180
commit
29880bdc23
9 changed files with 97 additions and 3 deletions
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [current]
|
## [current]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Custom chat commands can now be sent as replies, whispers and announcements. Due to some API shenanigans yet to be solved, the latter two will always be sent from your main account, not the bot account (if they are different)
|
||||||
|
|
||||||
## [3.2.0] - 2023-05-03
|
## [3.2.0] - 2023-05-03
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -116,6 +116,12 @@
|
||||||
"command-response-placeholder": "Hello {0}!",
|
"command-response-placeholder": "Hello {0}!",
|
||||||
"command-acl": "Access level",
|
"command-acl": "Access level",
|
||||||
"command-acl-help": "This specifies the minimum level, eg. if you choose VIPs, moderators and streamer can still use the command",
|
"command-acl-help": "This specifies the minimum level, eg. if you choose VIPs, moderators and streamer can still use the command",
|
||||||
|
"response-types": {
|
||||||
|
"chat": "Message",
|
||||||
|
"reply": "Reply",
|
||||||
|
"whisper": "Whisper",
|
||||||
|
"announce": "Announcement"
|
||||||
|
},
|
||||||
"acl": {
|
"acl": {
|
||||||
"everyone": "Everyone",
|
"everyone": "Everyone",
|
||||||
"subscribers": "Subscribers",
|
"subscribers": "Subscribers",
|
||||||
|
|
|
@ -112,7 +112,13 @@
|
||||||
"subscribers": "Abbonati",
|
"subscribers": "Abbonati",
|
||||||
"vip": "VIP"
|
"vip": "VIP"
|
||||||
},
|
},
|
||||||
"command-already-in-use": "Nome comando già in uso"
|
"command-already-in-use": "Nome comando già in uso",
|
||||||
|
"response-types": {
|
||||||
|
"announce": "Annuncio",
|
||||||
|
"chat": "Canale",
|
||||||
|
"reply": "Risposta",
|
||||||
|
"whisper": "Chat privata"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"bottimers": {
|
"bottimers": {
|
||||||
"add-button": "Nuovo timer",
|
"add-button": "Nuovo timer",
|
||||||
|
|
|
@ -34,10 +34,12 @@ export const accessLevels = [
|
||||||
|
|
||||||
export type AccessLevelType = (typeof accessLevels)[number];
|
export type AccessLevelType = (typeof accessLevels)[number];
|
||||||
|
|
||||||
|
export type ReplyType = 'chat' | 'reply' | 'whisper' | 'announce';
|
||||||
export interface TwitchBotCustomCommand {
|
export interface TwitchBotCustomCommand {
|
||||||
description: string;
|
description: string;
|
||||||
access_level: AccessLevelType;
|
access_level: AccessLevelType;
|
||||||
response: string;
|
response: string;
|
||||||
|
response_type: ReplyType;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { modules } from '~/store/api/reducer';
|
||||||
import {
|
import {
|
||||||
accessLevels,
|
accessLevels,
|
||||||
AccessLevelType,
|
AccessLevelType,
|
||||||
|
ReplyType,
|
||||||
TwitchBotCustomCommand,
|
TwitchBotCustomCommand,
|
||||||
} from '~/store/api/types';
|
} from '~/store/api/types';
|
||||||
import AlertContent from '../components/AlertContent';
|
import AlertContent from '../components/AlertContent';
|
||||||
|
@ -23,6 +24,8 @@ import {
|
||||||
InputBox,
|
InputBox,
|
||||||
Label,
|
Label,
|
||||||
MultiButton,
|
MultiButton,
|
||||||
|
MultiToggle,
|
||||||
|
MultiToggleItem,
|
||||||
NoneText,
|
NoneText,
|
||||||
PageContainer,
|
PageContainer,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
|
@ -185,11 +188,15 @@ function CommandDialog({
|
||||||
const [commands] = useModule(modules.twitchBotCommands);
|
const [commands] = useModule(modules.twitchBotCommands);
|
||||||
const [commandName, setCommandName] = useState(name ?? '');
|
const [commandName, setCommandName] = useState(name ?? '');
|
||||||
const [description, setDescription] = useState(item?.description ?? '');
|
const [description, setDescription] = useState(item?.description ?? '');
|
||||||
|
const [responseType, setResponseType] = useState(
|
||||||
|
item?.response_type ?? 'chat',
|
||||||
|
);
|
||||||
const [response, setResponse] = useState(item?.response ?? '');
|
const [response, setResponse] = useState(item?.response ?? '');
|
||||||
const [accessLevel, setAccessLevel] = useState(
|
const [accessLevel, setAccessLevel] = useState(
|
||||||
item?.access_level ?? 'everyone',
|
item?.access_level ?? 'everyone',
|
||||||
);
|
);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const replyTypes: ReplyType[] = ['chat', 'reply', 'whisper', 'announce'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogContent
|
<DialogContent
|
||||||
|
@ -207,6 +214,7 @@ function CommandDialog({
|
||||||
...item,
|
...item,
|
||||||
description,
|
description,
|
||||||
response,
|
response,
|
||||||
|
response_type: responseType,
|
||||||
access_level: accessLevel,
|
access_level: accessLevel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -248,6 +256,20 @@ function CommandDialog({
|
||||||
<Field spacing="narrow" size="fullWidth">
|
<Field spacing="narrow" size="fullWidth">
|
||||||
<Label htmlFor="command-response">
|
<Label htmlFor="command-response">
|
||||||
{t('pages.botcommands.command-response')}
|
{t('pages.botcommands.command-response')}
|
||||||
|
<MultiToggle
|
||||||
|
css={{ marginLeft: '0.5rem' }}
|
||||||
|
value={responseType}
|
||||||
|
type="single"
|
||||||
|
onValueChange={(newType) => {
|
||||||
|
setResponseType(newType as ReplyType);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{replyTypes.map((replyType) => (
|
||||||
|
<MultiToggleItem size="small" key={replyType} value={replyType}>
|
||||||
|
{t(`pages.botcommands.response-types.${replyType}`)}
|
||||||
|
</MultiToggleItem>
|
||||||
|
))}
|
||||||
|
</MultiToggle>
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
value={response}
|
value={response}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (c *Client) GetAuthorizationURL() string {
|
||||||
}
|
}
|
||||||
return c.API.GetAuthorizationURL(&helix.AuthorizationURLParams{
|
return c.API.GetAuthorizationURL(&helix.AuthorizationURLParams{
|
||||||
ResponseType: "code",
|
ResponseType: "code",
|
||||||
Scopes: []string{"bits:read channel:read:subscriptions channel:read:redemptions channel:read:polls channel:read:predictions channel:read:hype_train user_read chat:read chat:edit channel:moderate whispers:read whispers:edit moderator:read:chatters moderator:read:followers"},
|
Scopes: []string{"bits:read channel:read:subscriptions channel:read:redemptions channel:read:polls channel:read:predictions channel:read:hype_train user_read chat:read chat:edit channel:moderate whispers:read whispers:edit moderator:read:chatters moderator:read:followers user:manage:whispers moderator:manage:announcements"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,40 @@ func cmdCustom(bot *Bot, cmd string, data BotCustomCommand, message irc.PrivateM
|
||||||
bot.logger.Error("Failed to execute custom command template", zap.Error(err))
|
bot.logger.Error("Failed to execute custom command template", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch data.ResponseType {
|
||||||
|
case ResponseTypeDefault, ResponseTypeChat:
|
||||||
bot.Client.Say(message.Channel, buf.String())
|
bot.Client.Say(message.Channel, buf.String())
|
||||||
|
case ResponseTypeReply:
|
||||||
|
bot.Client.Reply(message.Channel, message.ID, buf.String())
|
||||||
|
case ResponseTypeWhisper:
|
||||||
|
client, err := bot.api.GetUserClient(false)
|
||||||
|
reply, err := client.SendUserWhisper(&helix.SendUserWhisperParams{
|
||||||
|
FromUserID: bot.api.User.ID,
|
||||||
|
ToUserID: message.User.ID,
|
||||||
|
Message: buf.String(),
|
||||||
|
})
|
||||||
|
if reply.Error != "" {
|
||||||
|
bot.logger.Error("Failed to send whisper", zap.String("code", reply.Error), zap.String("message", reply.ErrorMessage))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
bot.logger.Error("Failed to send whisper", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
case ResponseTypeAnnounce:
|
||||||
|
client, err := bot.api.GetUserClient(false)
|
||||||
|
reply, err := client.SendChatAnnouncement(&helix.SendChatAnnouncementParams{
|
||||||
|
BroadcasterID: bot.api.User.ID,
|
||||||
|
ModeratorID: bot.api.User.ID,
|
||||||
|
Message: buf.String(),
|
||||||
|
})
|
||||||
|
if reply.Error != "" {
|
||||||
|
bot.logger.Error("Failed to send announcement", zap.String("code", reply.Error), zap.String("message", reply.ErrorMessage))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
bot.logger.Error("Failed to send announcement", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) setupFunctions() {
|
func (b *Bot) setupFunctions() {
|
||||||
|
|
|
@ -44,6 +44,16 @@ const (
|
||||||
ChatActivityKey = "twitch/chat-activity"
|
ChatActivityKey = "twitch/chat-activity"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ResponseType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResponseTypeDefault ResponseType = ""
|
||||||
|
ResponseTypeChat ResponseType = "chat"
|
||||||
|
ResponseTypeWhisper ResponseType = "whisper"
|
||||||
|
ResponseTypeReply ResponseType = "reply"
|
||||||
|
ResponseTypeAnnounce ResponseType = "announce"
|
||||||
|
)
|
||||||
|
|
||||||
// BotCustomCommand is a definition of a custom command of the chatbot
|
// BotCustomCommand is a definition of a custom command of the chatbot
|
||||||
type BotCustomCommand struct {
|
type BotCustomCommand struct {
|
||||||
// Command description
|
// Command description
|
||||||
|
@ -57,6 +67,9 @@ type BotCustomCommand struct {
|
||||||
|
|
||||||
// Is the command enabled?
|
// Is the command enabled?
|
||||||
Enabled bool `json:"enabled" desc:"Is the command enabled?"`
|
Enabled bool `json:"enabled" desc:"Is the command enabled?"`
|
||||||
|
|
||||||
|
// How to respond to the user
|
||||||
|
ResponseType ResponseType `json:"response_type" desc:"How to respond to the user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomCommandsKey = "twitch/bot-custom-commands"
|
const CustomCommandsKey = "twitch/bot-custom-commands"
|
||||||
|
|
|
@ -80,4 +80,12 @@ var Enums = interfaces.EnumMap{
|
||||||
ALTStreamer,
|
ALTStreamer,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ResponseType": interfaces.Enum{
|
||||||
|
Values: []any{
|
||||||
|
ResponseTypeChat,
|
||||||
|
ResponseTypeReply,
|
||||||
|
ResponseTypeWhisper,
|
||||||
|
ResponseTypeAnnounce,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue