aboutsummaryrefslogblamecommitdiffstats
path: root/main.go
blob: 9d09932f60284bd1a908abcce1905ec6efa6e8c6 (plain) (tree)
1
2
3
4
5



               
                 








                       
                                               



                                         
                        














                                   
































                                                                 
                                                                     















                                                                                          

                                                               














                                                                                                          




                                                                                                                                     

























                                                                                                          










                                                                                                                                             








                                                                 
                                                                                              
























                                                          
package main

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/alecthomas/chroma/v2/quick"
	"github.com/lrstanley/girc"
	"github.com/pelletier/go-toml/v2"
)

type TomlConfig struct {
	IrcServer           string
	IrcPort             int
	IrcNick             string
	IrcSaslUser         string
	IrcSaslPass         string
	IrcChannel          string
	OllamaEndpoint      string
	OllamaTemp          float64
	OllamaSystem        string
	RequestTimeout      int
	MillaReconnectDelay int
	EnableSasl          bool
	Model               string
	ChromaStyle         string
	ChromaFormatter     string
}

type OllamaResponse struct {
	Response string `json:"response"`
}

type OllamaRequestOptions struct {
	Temperature float64 `json:"temperature"`
}

type OllamaRequest struct {
	Model   string               `json:"model"`
	System  string               `json:"system"`
	Prompt  string               `json:"prompt"`
	Stream  bool                 `json:"stream"`
	Format  string               `json:"format"`
	Options OllamaRequestOptions `json:"options"`
}

func runIRC(appConfig TomlConfig, ircChan chan *girc.Client) {
	irc := girc.New(girc.Config{
		Server:    appConfig.IrcServer,
		Port:      appConfig.IrcPort,
		Nick:      appConfig.IrcNick,
		User:      appConfig.IrcNick,
		Name:      appConfig.IrcNick,
		SSL:       true,
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})

	saslUser := appConfig.IrcSaslUser
	saslPass := appConfig.IrcSaslPass

	if appConfig.EnableSasl && saslUser != "" && saslPass != "" {
		irc.Config.SASL = &girc.SASLPlain{
			User: appConfig.IrcSaslUser,
			Pass: appConfig.IrcSaslPass,
		}
	}

	irc.Handlers.AddBg(girc.CONNECTED, func(c *girc.Client, e girc.Event) {
		c.Cmd.Join(appConfig.IrcChannel)
	})

	irc.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, event girc.Event) {
		if strings.HasPrefix(event.Last(), appConfig.IrcNick+": ") {
			prompt := strings.TrimPrefix(event.Last(), appConfig.IrcNick+": ")
			log.Println(prompt)

			ollamaRequest := OllamaRequest{
				Model:  appConfig.Model,
				System: appConfig.OllamaSystem,
				Prompt: prompt,
				Stream: false,
				Format: "json",
				Options: OllamaRequestOptions{
					Temperature: appConfig.OllamaTemp,
				},
			}

			jsonPayload, err := json.Marshal(ollamaRequest)
			if err != nil {
				client.Cmd.ReplyTo(event, girc.Fmt(fmt.Sprintf("error: %s", err.Error())))

				return
			}

			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(appConfig.RequestTimeout)*time.Second)
			defer cancel()

			request, err := http.NewRequest(http.MethodPost, appConfig.OllamaEndpoint, bytes.NewBuffer(jsonPayload))
			request = request.WithContext(ctx)
			if err != nil {
				client.Cmd.ReplyTo(event, girc.Fmt(fmt.Sprintf("error: %s", err.Error())))

				return
			}

			request.Header.Set("Content-Type", "application/json")

			httpClient := http.Client{}

			response, err := httpClient.Do(request)
			if err != nil {
				client.Cmd.ReplyTo(event, girc.Fmt(fmt.Sprintf("error: %s", err.Error())))

				return
			}
			defer response.Body.Close()

			var ollamaResponse OllamaResponse
			err = json.NewDecoder(response.Body).Decode(&ollamaResponse)
			if err != nil {
				client.Cmd.ReplyTo(event, girc.Fmt(fmt.Sprintf("error: %s", err.Error())))

				return
			}

			var writer bytes.Buffer
			err = quick.Highlight(&writer, ollamaResponse.Response, "markdown", appConfig.ChromaFormatter, appConfig.ChromaStyle)
			if err != nil {
				client.Cmd.ReplyTo(event, girc.Fmt(fmt.Sprintf("error: %s", err.Error())))

				return
			}

			fmt.Println(writer.String())
			client.Cmd.ReplyTo(event, girc.Fmt(writer.String()))
			// client.Cmd.ReplyTo(event, girc.Fmt(ollamaResponse.Response))
		}
	})

	ircChan <- irc

	for {
		if err := irc.Connect(); err != nil {
			log.Println(err)
			log.Println("reconnecting in 30 seconds")
			time.Sleep(time.Duration(appConfig.MillaReconnectDelay) * time.Second)
		} else {
			return
		}
	}
}

func main() {
	var appConfig TomlConfig

	data, err := os.ReadFile("/opt/milla/config.toml")
	if err != nil {
		log.Fatal(err)
	}

	err = toml.Unmarshal(data, &appConfig)
	if err != nil {
		log.Fatal(err)
	}

	log.Println(appConfig)

	ircChan := make(chan *girc.Client, 1)

	runIRC(appConfig, ircChan)
}