<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/slugalisk/overrustlelogs/common"
)

// TwitchHub ...
type TwitchHub struct {
	chatLock       sync.RWMutex
	chats          []*common.Twitch
	chLock         sync.RWMutex
	channels       []string
	logHandler     func(m <-chan *common.Message)
	admins         map[string]struct{}
	commandChannel string
	quit           chan struct{}
}

// NewTwitchLogger ...
func NewTwitchLogger(f func(m <-chan *common.Message)) *TwitchHub {
	t := &TwitchHub{
		logHandler:     f,
		admins:         make(map[string]struct{}),
		commandChannel: common.GetConfig().Twitch.CommandChannel,
		quit:           make(chan struct{}, 1),
	}

	admins := common.GetConfig().Twitch.Admins
	for _, a := range admins {
		t.admins[a] = struct{}{}
	}

	d, err := ioutil.ReadFile(common.GetConfig().Twitch.ChannelListPath)
	if err != nil {
		log.Fatalf("unable to read channels %s", err)
	}
	if err := json.Unmarshal(d, &t.channels); err != nil {
		log.Fatalf("unable to read channels %s", err)
	}
	return t
}

// Start ...
func (t *TwitchHub) Start() {
	var c int
	for _, channel := range t.channels {
		select {
		case <-t.quit:
			return
		default:
		}
		err := t.join(channel, false)
		if err != nil {
			log.Printf("%v", err)
			continue
		}
		c++
	}
	log.Printf("joined %d chats, wew lad :^)\n", c)
}

// Stop ...
func (t *TwitchHub) Stop() {
	close(t.quit)
	var wg sync.WaitGroup

	t.chatLock.Lock()
	wg.Add(len(t.chats))
	for i, c := range t.chats {
		log.Printf("stopping chat: %d\n", i)
		go c.Stop(&wg)
	}
	t.chatLock.Unlock()
	wg.Wait()
}

func (t *TwitchHub) runCommand(c *common.Twitch, m *common.Message) {
	if _, ok := t.admins[m.Nick]; !ok || m.Type != "MSG" {
		return
	}

	parts := strings.Split(strings.ToLower(m.Data), " ")
	switch parts[0] {
	case "!join":
		if err := t.join(parts[1], true); err != nil {
			c.Message(m.Channel, err.Error())
			return
		}
		c.Message(m.Channel, fmt.Sprintf("Logging %s", parts[1]))
	case "!leave":
		if err := t.leave(parts[1]); err != nil {
			c.Message(m.Channel, fmt.Sprintf("Not logging %s", parts[1]))
			return
		}
		c.Message(m.Channel, fmt.Sprintf("Leaving %s", parts[1]))
	}
}

func (t *TwitchHub) join(ch string, init bool) error {
	if inSlice(t.channels, ch) && init {
		return fmt.Errorf("already logging %s", ch)
	}
	if !channelExists(ch) && init {
		return fmt.Errorf("%s doesn't exist my dude", ch)
	}
	if init {
		t.addChannel(ch)
		go t.saveChannels()
	}
	t.chatLock.Lock()
	var chat *common.Twitch
	for _, c := range t.chats {
		if len(c.Channels()) >= common.MaxChannelsPerChat {
			continue<commit_msg>print leave and join errors<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/slugalisk/overrustlelogs/common"
)

// TwitchHub ...
type TwitchHub struct {
	chatLock       sync.RWMutex
	chats          []*common.Twitch
	chLock         sync.RWMutex
	channels       []string
	logHandler     func(m <-chan *common.Message)
	admins         map[string]struct{}
	commandChannel string
	quit           chan struct{}
}

// NewTwitchLogger ...
func NewTwitchLogger(f func(m <-chan *common.Message)) *TwitchHub {
	t := &TwitchHub{
		logHandler:     f,
		admins:         make(map[string]struct{}),
		commandChannel: common.GetConfig().Twitch.CommandChannel,
		quit:           make(chan struct{}, 1),
	}

	admins := common.GetConfig().Twitch.Admins
	for _, a := range admins {
		t.admins[a] = struct{}{}
	}

	d, err := ioutil.ReadFile(common.GetConfig().Twitch.ChannelListPath)
	if err != nil {
		log.Fatalf("unable to read channels %s", err)
	}
	if err := json.Unmarshal(d, &t.channels); err != nil {
		log.Fatalf("unable to read channels %s", err)
	}
	return t
}

// Start ...
func (t *TwitchHub) Start() {
	var c int
	for _, channel := range t.channels {
		select {
		case <-t.quit:
			return
		default:
		}
		err := t.join(channel, false)
		if err != nil {
			log.Printf("%v", err)
			continue
		}
		c++
	}
	log.Printf("joined %d chats, wew lad :^)\n", c)
}

// Stop ...
func (t *TwitchHub) Stop() {
	close(t.quit)
	var wg sync.WaitGroup

	t.chatLock.Lock()
	wg.Add(len(t.chats))
	for i, c := range t.chats {
		log.Printf("stopping chat: %d\n", i)
		go c.Stop(&wg)
	}
	t.chatLock.Unlock()
	wg.Wait()
}

func (t *TwitchHub) runCommand(c *common.Twitch, m *common.Message) {
	if _, ok := t.admins[m.Nick]; !ok || m.Type != "MSG" {
		return
	}

	parts := strings.Split(strings.ToLower(m.Data), " ")
	switch parts[0] {
	case "!join":
		if err := t.join(parts[1], true); err != nil {
			log.Println(c.Message(m.Channel, err.Error()))
			return
		}
		c.Message(m.Channel, fmt.Sprintf("Logging %s", strings.TrimSpace(parts[1])))
	case "!leave":
		if err := t.leave(parts[1]); err != nil {
			log.Println(c.Message(m.Channel, fmt.Sprintf("Not logging %s", strings.TrimSpace(parts[1]))))
			return
		}
		c.Message(m.Channel, fmt.Sprintf("Leaving %s", parts[1]))
	}
}

func (t *TwitchHub) join(ch string, init bool) error {
	if inSlice(t.channels, ch) && init {
		return fmt.Errorf("already logging %s", ch)
	}
	if !channelExists(ch) && init {
		return fmt.Errorf("%s doesn't exist my dude", ch)
	}
	if init {
		t.addChannel(ch)
		go t.saveChannels()
	}
	t.chatLock.Lock()
	var chat *common.Twitch
	for _, c := range t.chats {
		if len(c.Channels()) >= common.MaxChannelsPerChat {
			continue
{'fraction_non_alphanumeric': 0.17451624680540342, 'fraction_numerical': 0.002555677254472435, 'mean_word_length': 3.172886519421173, 'pattern_counts': {'":': 4, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2683443', 'n_tokens_mistral': 2275, 'n_tokens_neox': 2150, 'n_words': 489}