Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Pipello committed Apr 9, 2024
0 parents commit ac3651b
Show file tree
Hide file tree
Showing 10 changed files with 401 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.env
news.json
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.PHONY: fmt
fmt:
go mod tidy
go fmt ./...

.PHONY: refresh
refresh:
go run cmd/refresh_news/main.go
58 changes: 58 additions & 0 deletions cmd/refresh_news/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"encoding/json"
"log"
"net/http"
"os"

"sarcastick/pkg/news"

"golang.org/x/net/html"
)

func main() {
response, err := http.Get("https://www.expats.cz")
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
log.Fatalf("HTTP request failed with status code %d", response.StatusCode)
}

htmlBody, err := html.Parse(response.Body)
if err != nil {
log.Fatal(err)
}
link := news.FindNewsLink(htmlBody)
if link == "" {
log.Fatal("No link found")
}

response, err = http.Get("https://www.expats.cz" + link)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
log.Fatalf("HTTP request failed with status code %d", response.StatusCode)
}

htmlBody, err = html.Parse(response.Body)
if err != nil {
log.Fatal(err)
}

ct := news.ExtractContentWithTitle(htmlBody)
file, err := os.Create("news.json")
if err != nil {
log.Fatal(err)
}
err = json.NewEncoder(file).Encode(ct)
if err != nil {
log.Fatal(err)
}
}
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module sarcastick

go 1.21

require golang.org/x/net v0.24.0

require github.com/go-telegram/bot v1.2.1

require github.com/sashabaranov/go-openai v1.20.5
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/go-telegram/bot v1.2.1 h1:FkrixLCtMtPUQAN4plXdNElbhkdXkx2p68YPXKBruDg=
github.com/go-telegram/bot v1.2.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
github.com/sashabaranov/go-openai v1.20.5 h1:Sab4nzBLtoyxm4jRqH9G9pkIh50WpBFacPTEpruPB6o=
github.com/sashabaranov/go-openai v1.20.5/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
43 changes: 43 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"context"
"log"
"os"
"regexp"
"sarcastick/pkg/news"
"sarcastick/pkg/openai"
"strings"

"github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
)

var detailsRegex = regexp.MustCompile("^/details [0-9]*$")
var sarcasmRegex = regexp.MustCompile("^/sarcasm [0-9]*$")

func main() {
b, err := bot.New(os.Getenv("TELEGRAM_BOT_KEY"))
if err != nil {
log.Fatal(err)
}
client := openai.NewClient()
b.RegisterHandler(bot.HandlerTypeMessageText, "/start", bot.MatchTypeExact, helpHandler)
b.RegisterHandler(bot.HandlerTypeMessageText, "/help", bot.MatchTypeExact, helpHandler)
b.RegisterHandler(bot.HandlerTypeMessageText, "/news", bot.MatchTypeExact, news.SummaryHandler)
b.RegisterHandlerRegexp(bot.HandlerTypeMessageText, detailsRegex, news.DetailsHandler)
b.RegisterHandlerRegexp(bot.HandlerTypeMessageText, sarcasmRegex, client.SarcasmHandler)
b.Start(context.Background())
}

func helpHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
var sb strings.Builder
sb.WriteString("- use /news to get the main titles of the day (given with their ID)\n")
sb.WriteString("- use /details with the ID of the news to know more\n")
sb.WriteString("- use /sarcasm with the ID of the news to generate a sarcastic comment on the news\n")
sb.WriteString("- use /help to show this message \n")
b.SendMessage(ctx, &bot.SendMessageParams{
ChatID: update.Message.Chat.ID,
Text: sb.String(),
})
}
85 changes: 85 additions & 0 deletions pkg/news/extract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package news

import (
"slices"
"strings"

"golang.org/x/net/html"
)

func FindNewsLink(n *html.Node) string {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key == "href" && strings.Contains(a.Val, "czech-news-in-brief") {
return a.Val
}
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if l := FindNewsLink(c); l != "" {
return l
}
}
return ""
}

func ExtractContentWithTitle(n *html.Node) []News {
if n.Type == html.ElementNode && n.Data == "div" && slices.ContainsFunc(n.Attr, containContentClass) {
ct := []News{}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode && c.Data == "div" && slices.ContainsFunc(c.Attr, containTitleClass) {
ct = append(ct, News{
Title: extractText(c),
Content: extractContent(c.NextSibling),
})
}
}
if len(ct) > 0 {
return ct
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if ct := ExtractContentWithTitle(c); len(ct) > 0 {
return ct
}
}
return nil
}

func containTextWrapperClass(a html.Attribute) bool {
return a.Key == "class" && strings.Contains(a.Val, "widget text")
}

func containTitleClass(a html.Attribute) bool {
return a.Key == "class" && strings.Contains(a.Val, "headinglevel2")
}

func containContentClass(a html.Attribute) bool {
return a.Key == "class" && strings.Contains(a.Val, "content")
}

func extractContent(n *html.Node) string {
r := ""
for c := n; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode && c.Data == "div" && slices.ContainsFunc(c.Attr, containTextWrapperClass) {
r += extractText(c.FirstChild)
}
if c.Type == html.ElementNode && c.Data == "div" && slices.ContainsFunc(c.Attr, containTitleClass) {
break
}
}
return r
}

func extractText(n *html.Node) string {
if n.Type == html.TextNode {
return n.Data
}
r := ""
if n.Type == html.ElementNode {
for c := n.FirstChild; c != nil; c = c.NextSibling {
r += extractText(c)
}
}
return r
}
49 changes: 49 additions & 0 deletions pkg/news/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package news

import (
"context"
"fmt"
"strconv"
"strings"

"github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
)

func SummaryHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
t, err := getSummary()
if err != nil {
fmt.Println(err.Error())
return
}
b.SendMessage(ctx, &bot.SendMessageParams{
ChatID: update.Message.Chat.ID,
Text: t,
})
}

func DetailsHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
id, err := ParseID(update)
if err != nil {
fmt.Println("Error", err.Error())
return
}
n, err := GetNewsById(id)
if err != nil {
fmt.Println("Error", err.Error())
return
}
var qb strings.Builder
qb.WriteString(n.Title + "\n\n")
qb.WriteString(n.Content)
b.SendMessage(ctx, &bot.SendMessageParams{
ChatID: update.Message.Chat.ID,
Text: qb.String(),
})
}

func ParseID(update *models.Update) (int, error) {
text := update.Message.Text
id := strings.Split(text, " ")[1]
return strconv.Atoi(id)
}
51 changes: 51 additions & 0 deletions pkg/news/news.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package news

import (
"encoding/json"
"fmt"
"os"
"strings"
)

type News struct {
Title string
Content string
}

func getSummary() (string, error) {
allNews, err := getAllNews()
if err != nil {
return "", err
}
var sb strings.Builder
sb.WriteString("TODAY'S NEWS (based on expat.cz)\n")
for i, n := range allNews {
sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, n.Title))
}
sb.WriteString("Use /details {news} or /sarcasm {news} for more")
return sb.String(), nil
}

func getAllNews() ([]*News, error) {
file, err := os.Open("news.json")
if err != nil {
return nil, err
}
var allNews []*News
err = json.NewDecoder(file).Decode(&allNews)
if err != nil {
return nil, err
}
return allNews, nil
}

func GetNewsById(id int) (*News, error) {
allNews, err := getAllNews()
if err != nil {
return nil, err
}
if len(allNews) < id {
return nil, nil
}
return allNews[id-1], nil
}
Loading

0 comments on commit ac3651b

Please sign in to comment.