frosty/lib/models/irc.dart

126 lines
4.2 KiB
Dart
Raw Normal View History

import 'package:flutter/foundation.dart';
import 'package:frosty/api/irc_api.dart';
/// The object representation of a Twitch IRC message.
class IRCMessage {
final Map<String, String> tags;
final String? user;
Command command;
String? message;
IRCMessage({required this.tags, required this.command, required this.user, required this.message});
/// Parses an IRC string and returns its corresponding [IRCMessage] object.
factory IRCMessage.fromString(String whole) {
// We have three parts:
// 1. The tags of the IRC message
// 2. The metadata (user, command, and channel)
// 3. The message itself
// First, slice the message tags (1) and set aside the rest for later.
// Each part is separated by a space, so we'll start off by breaking off the tags and parsing them.
// Obtain the index to break off the tags.
final tagAndIrcMessageDivider = whole.indexOf(' ');
// Get the tags substring and escape characters.
// IRC messages escape spaces with \s.
final tags = whole.substring(1, tagAndIrcMessageDivider).replaceAll('\\s', ' ');
// Next, parse and map the tags.
final mappedTags = <String, String>{};
// Loop through each tag and store their key value pairs into the map.
for (final tag in tags.split(';')) {
// Skip if the tag has no value.
if (tag.endsWith('=')) continue;
final tagSplit = tag.split('=');
mappedTags[tagSplit[0]] = tagSplit[1];
}
// Now we'll parse the message itself.
// Obtain the entire substring after the tags and excluding the first : (colon)
final ircMessage = whole.substring(tagAndIrcMessageDivider + 2);
// Split each section of the message (part 2 and its subparts and part 3).
// Index 0 contains the username of sender (user) or tmi.twitch.tv depending on the command
// Index 1 is command type
// Index 2 is #channel name that we are currently connected to
// Index 3 and beyond is the :message (word by word) that is sent by the user or empty depending on the command
final splitMessage = ircMessage.split(' ');
// If the username exists, set it.
// tmi.twitch.tv means the message was sent by Twitch rather than a user, so will be irrelevant.
final String? user = splitMessage[0] == 'tmi.twitch.tv' ? null : splitMessage[0].substring(0, splitMessage[0].indexOf('!'));
// If there is an associated message, set it.
final String? message = splitMessage.length > 3 ? splitMessage.sublist(3).join(' ').substring(1) : null;
// Check and parse the command.
// The majority of messages will be PRIVMSG, so check that first.
final Command messageCommand;
switch (splitMessage[1]) {
case 'PRIVMSG':
messageCommand = Command.privateMessage;
break;
case 'CLEARCHAT':
messageCommand = Command.clearChat;
break;
case 'CLEARMSG':
messageCommand = Command.clearMessage;
break;
case 'USERNOTICE':
messageCommand = Command.userNotice;
break;
case 'ROOMSTATE':
messageCommand = Command.roomState;
break;
case 'USERSTATE':
messageCommand = Command.userState;
break;
case 'GLOBALUSERSTATE':
messageCommand = Command.globalUserState;
break;
default:
debugPrint('Unknown command: $splitMessage[1]');
messageCommand = Command.none;
}
return IRCMessage(
tags: mappedTags,
command: messageCommand,
user: user,
message: message,
);
}
}
/// The object representation of the IRC ROOMSTATE message.
class ROOMSTATE {
2021-10-23 00:25:22 -04:00
final String emoteOnly;
final String followersOnly;
final String r9k;
final String slowMode;
final String subMode;
ROOMSTATE({
this.emoteOnly = "0",
this.followersOnly = "0",
this.r9k = "0",
this.slowMode = "0",
this.subMode = "0",
});
/// Create a new copy with the paremeters from the provided [IRCMessage]
2021-10-23 00:25:22 -04:00
ROOMSTATE copyWith(IRCMessage ircMessage) {
return ROOMSTATE(
2021-10-23 00:25:22 -04:00
emoteOnly: ircMessage.tags['emote-only'] ?? emoteOnly,
followersOnly: ircMessage.tags['followers-only'] ?? followersOnly,
r9k: ircMessage.tags['r9k'] ?? r9k,
slowMode: ircMessage.tags['slow'] ?? slowMode,
subMode: ircMessage.tags['subs-only'] ?? subMode,
);
}
}