robotty/dank-twitch-irc

View on GitHub
lib/operations/say.ts

Summary

Maintainability
A
30 mins
Test Coverage
A
100%
import { awaitResponse } from "../await/await-response";
import { SingleConnection } from "../client/connection";
import { MessageError } from "../client/errors";
import { NoticeMessage } from "../message/twitch-types/notice";
import { UserstateMessage } from "../message/twitch-types/userstate";
import { sendPrivmsg } from "./privmsg";

export function removeCommands(message: string): string {
  if (message.startsWith(".") || message.startsWith("/")) {
    return `/ ${message}`;
  } else {
    return message;
  }
}

export class SayError extends MessageError {
  public failedChannelName: string;
  public messageText: string;
  public action: boolean;

  public constructor(
    failedChannelName: string,
    failedMessage: string,
    action: boolean,
    message?: string,
    cause?: Error
  ) {
    super(message, cause);
    this.failedChannelName = failedChannelName;
    this.messageText = failedMessage;
    this.action = action;
  }
}

const badNoticeIDs = [
  "msg_banned", // You are permanently banned from talking in <channel>.
  "msg_bad_characters", // Your message was not sent because it contained too many unprocessable characters.
  // If you believe this is an error, please rephrase and try again.
  "msg_channel_blocked", // Your message was not sent because your account is not in good standing in this channel.
  "msg_channel_suspended", // This channel has been suspended.
  "msg_duplicate", // Your message was not sent because it is identical to the previous one you sent,
  // less than 30 seconds ago.
  "msg_emoteonly", // This room is in emote only mode. You can find your currently available emoticons using
  // the smiley in the chat text area.
  "msg_facebook", // You must Facebook Connect to send messages to this channel. You can Facebook Connect in
  // your Twitch settings under the connections tab.
  "msg_followersonly", // This room is in <duration> followers-only mode. Follow <channel> to join the community!
  "msg_followersonly_followed", // This room is in <duration1> followers-only mode. You have been following for
  // <duration2>. Continue following to chat!
  "msg_followersonly_zero", // This room is in followers-only mode. Follow <channel> to join the community!
  "msg_r9k", // This room is in r9k mode and the message you attempted to send is not unique.
  "msg_ratelimit", // Your message was not sent because you are sending messages too quickly.
  "msg_rejected", //     Hey! Your message is being checked by mods and has not been sent.
  "msg_rejected_mandatory", // Your message wasn't posted due to conflicts with the channel's moderation settings.
  "msg_room_not_found", // The room was not found.
  "msg_slowmode", // This room is in slow mode and you are sending messages too quickly. You will be able to
  // talk again in <number> seconds.
  "msg_subsonly", // This room is in subscribers only mode. To talk, purchase a channel subscription at
  // https://www.twitch.tv/products/<broadcaster login name>/ticket?ref=subscriber_only_mode_chat.
  "msg_suspended", // Your account has been suspended.
  "msg_timedout", // You are banned from talking in <channel> for <number> more seconds.
  "msg_verified_email", //     This room requires a verified email address to chat. Please verify your email at
  // https://www.twitch.tv/settings/profile.
];

export async function say(
  conn: SingleConnection,
  channelName: string,
  messageText: string,
  action = false
): Promise<UserstateMessage> {
  let command;
  let errorMessage;
  let errorType: (message?: string, cause?: Error) => Error;
  if (action) {
    command = `/me ${messageText}`;
    errorMessage = `Failed to say [#${channelName}]: /me ${messageText}`;
    errorType = (msg, cause) =>
      new SayError(channelName, messageText, true, msg, cause);
  } else {
    command = removeCommands(messageText);
    errorMessage = `Failed to say [#${channelName}]: ${messageText}`;
    errorType = (msg, cause) =>
      new SayError(channelName, messageText, false, msg, cause);
  }
  sendPrivmsg(conn, channelName, command);

  return awaitResponse(conn, {
    success: (msg) =>
      msg instanceof UserstateMessage && msg.channelName === channelName,
    failure: (msg) =>
      msg instanceof NoticeMessage &&
      msg.channelName === channelName &&
      badNoticeIDs.includes(msg.messageID!),
    errorType,
    errorMessage,
  }) as Promise<UserstateMessage>;
}

export async function me(
  conn: SingleConnection,
  channelName: string,
  message: string
): Promise<UserstateMessage> {
  return say(conn, channelName, message, true);
}