5 use Illuminate\Database\Eloquent\Factories\HasFactory;
6 use Illuminate\Database\Eloquent\Model;
7 use Illuminate\Support\Arr;
8 use Illuminate\Support\Str;
9 use LanguageDetection\Language;
11 class ChatLog extends Model {
15 public function channel() {
16 return $this->belongsTo(Channel::class);
19 public function user() {
20 return $this->belongsTo(User::class);
23 public function getTextWithoutEmotes() {
24 $text = $this->text_content;
25 if (isset($this->tags['emotes']) && !empty($this->tags['emotes'])) {
26 $emotes = explode('/', $this->tags['emotes']);
27 foreach ($emotes as $emote) {
28 $set = explode(':', $emote);
29 $positions = explode(',', $set[1]);
30 foreach ($positions as $position) {
31 $coords = explode('-', $position);
32 for ($i = intval($coords[0]); $i <= intval($coords[1]); ++$i) {
38 return trim(preg_replace('/\s+/', ' ', $text));
41 public function evaluate() {
42 $this->evaluateUser();
43 $this->evaluateChannel();
45 if (is_null($this->nick)) {
46 $this->type = 'system';
49 if (in_array($this->nick, ['horstiebot', 'localhorsttv'])) {
54 if ($this->command == 'PRIVMSG') {
55 if (static::isKnownBot($this->nick)) {
57 } else if (substr($this->params[0], 0, 1) == '#') {
62 $this->text_content = $this->params[1];
63 $this->detectLanguage();
64 if ($this->scanForSpam()) {
67 $this->classification = static::classify($this->text_content);
71 throw new \Exception('unidentified message');
74 public static function isKnownBot($nick) {
75 return in_array(strtolower($nick), [
80 'pokemoncommunitygame',
89 public static function classify($text) {
91 return 'unclassified';
93 if (is_numeric(trim($text))) {
96 $rawText = strtolower(preg_replace('/[^\w]/', '', $text));
97 $tokenizedText = preg_split('/\s+/', strtolower(trim($text)));
98 if (Str::startsWith($rawText, 'gg') || Str::endsWith($rawText, 'gg')) {
101 if (Str::contains($rawText, ['glgl', 'glhf', 'hfgl'])) {
104 if (Str::contains($rawText, ['haha', 'hehe', 'hihi', 'kekw', 'lol', 'lul', 'xd'])) {
107 if (Str::startsWith($rawText, ['ahoi', 'hallo', 'hello', 'hi ', 'huhu']) || Str::endsWith($rawText, ['hi', 'wave'])) {
110 if (Str::contains($rawText, ['pog', 'wow'])) {
113 if (Str::contains($rawText, ['hype'])) {
116 if (Str::startsWith($rawText, 'o7') || Str::endsWith($rawText, 'o7') || Str::contains($rawText, 'salut')) {
119 return 'unclassified';
122 protected function evaluateUser() {
125 protected function evaluateChannel() {
126 if (empty($this->params)) {
127 $this->channel()->associate(null);
130 $cname = $this->params[0];
131 if (substr($cname, 0, 1) != '#') {
134 $channel = Channel::firstWhere('twitch_chat', '=', $cname);
135 $this->channel()->associate($channel);
138 protected function detectLanguage() {
139 $languages = ['de', 'en', 'es', 'fr'];
140 if (!is_null($this->channel)) {
141 $languages = array_values($this->channel->languages);
142 if (!in_array('en', $languages)) {
146 $detector = (new Language($languages))->detect($this->getTextWithoutEmotes());
147 $scores = $detector->close();
148 $lang = strval($detector);
149 //var_dump($scores, $lang, $this->text_content);
150 if (!empty($lang) && $scores[$lang] > 0.4) {
151 $this->detected_language = $lang;
155 public static function spammyText($raw_text) {
156 $text = strtolower($raw_text);
157 if (substr($text, 0, 1) == '!') {
160 if (strpos($text, '$') !== false) {
163 if (strpos($text, '€') !== false) {
166 if (strpos($text, '@') !== false) {
169 if (strpos($text, '://') !== false) {
172 if (strpos($text, 'followers') !== false) {
175 if (strpos($text, 'horstie') !== false) {
178 if (strpos($text, 'promotion') !== false) {
181 if (strpos($text, 'viewers') !== false) {
184 if (strpos($text, 'view ers') !== false) {
187 if (strpos($text, 'vielen dank für den raid') !== false) {
190 if (strpos($text, 'willkommen auf starbase 47') !== false) {
196 protected function scanForSpam() {
197 if (is_numeric($this->text_content)) {
200 return static::spammyText($this->text_content);
204 'banned' => 'boolean',
207 'user_id' => 'string',
210 protected $fillable = [