2023-10-16 00:03:52 +03:00
|
|
|
|
//
|
|
|
|
|
// анекдоты категории /b
|
|
|
|
|
//
|
|
|
|
|
// Руслан <im@fruw.org>, 2023
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Message struct {
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
Text interface{} `json:"text"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Channel struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
Messages []Message `json:"messages"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
showVersion bool
|
|
|
|
|
port int
|
|
|
|
|
content string
|
|
|
|
|
html string
|
|
|
|
|
jsonParse string
|
|
|
|
|
messages Channel
|
|
|
|
|
maxID int
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2023-10-16 00:23:43 +03:00
|
|
|
|
version = "1.0.2"
|
2023-10-16 00:03:52 +03:00
|
|
|
|
cyan = "\033[1;36m"
|
|
|
|
|
reset = "\033[0m"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
flag.BoolVar(&showVersion, "v", false, "")
|
|
|
|
|
flag.BoolVar(&showVersion, "version", false, "")
|
|
|
|
|
flag.IntVar(&port, "p", 9999, "")
|
|
|
|
|
flag.IntVar(&port, "port", 9999, "")
|
|
|
|
|
flag.StringVar(&content, "c", "/etc/banek/content.json", "")
|
|
|
|
|
flag.StringVar(&content, "content", "/etc/banek/content.json", "")
|
|
|
|
|
flag.StringVar(&html, "h", "/etc/banek/template.html", "")
|
|
|
|
|
flag.StringVar(&html, "html", "/etc/banek/template.html", "")
|
|
|
|
|
flag.StringVar(&jsonParse, "j", "", "")
|
|
|
|
|
flag.StringVar(&jsonParse, "json-parse", "", "")
|
|
|
|
|
|
|
|
|
|
flag.Usage = func() {
|
|
|
|
|
fmt.Printf("banek %s%s%s - анекдоты категории /b\n\n", cyan, version, reset)
|
|
|
|
|
fmt.Printf("%s-h%s, %s--help%s Показать справку banek и завершить работу\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Printf("%s-v%s, %s--version%s Показать версию banek и завершить работу\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Printf("%s-p%s, %s--port%s Порт, на котором будет запущен сервис (по-умолчанию 9999)\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Printf("%s-c%s, %s--content%s Путь до json-файла с анеками (по-умолчанию /etc/banek/content.json)\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Printf("%s-h%s, %s--html%s Путь до html-шаблона (по-умолчанию /etc/banek/template.html)\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Printf("%s-j%s, %s--json-parse%s Спарсить выгрузку из группы в json\n\n", cyan, reset, cyan, reset)
|
|
|
|
|
fmt.Println("Руслан, <im@fruw.org>, 2023")
|
|
|
|
|
}
|
|
|
|
|
flag.Parse()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
if showVersion {
|
|
|
|
|
fmt.Printf("banek %s%s%s\n", cyan, version, reset)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if jsonParse != "" {
|
|
|
|
|
parseJSON()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
|
|
|
|
|
err := loadMessages(content)
|
|
|
|
|
if err != nil {
|
2023-10-16 00:23:43 +03:00
|
|
|
|
fmt.Println("Ошибка чтения JSON:", err)
|
2023-10-16 00:03:52 +03:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = ioutil.ReadFile(html)
|
|
|
|
|
if err != nil {
|
2023-10-16 00:23:43 +03:00
|
|
|
|
fmt.Println("Ошибка чтения HTML:", err)
|
2023-10-16 00:03:52 +03:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http.HandleFunc("/", handleRequest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Ошибка сервиса:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("Сервис запущен на порту :%d\n", port)
|
|
|
|
|
http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseJSON() {
|
2023-10-16 00:23:43 +03:00
|
|
|
|
jsonData, err := ioutil.ReadFile(jsonParse)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Ошибка чтения %d: %d", content, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var channel Channel
|
|
|
|
|
if err := json.Unmarshal(jsonData, &channel); err != nil {
|
|
|
|
|
fmt.Println("Ошибка парсинга JSON:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
messageNumber := 1
|
|
|
|
|
for i, message := range channel.Messages {
|
|
|
|
|
if message.Text != nil {
|
|
|
|
|
switch text := message.Text.(type) {
|
|
|
|
|
case string:
|
|
|
|
|
if text != "" {
|
|
|
|
|
channel.Messages[i].ID = messageNumber
|
|
|
|
|
channel.Messages[i].Text = fmt.Sprintf("%s", text)
|
|
|
|
|
messageNumber++
|
|
|
|
|
}
|
|
|
|
|
case []interface{}:
|
|
|
|
|
var newText []string
|
|
|
|
|
for _, item := range text {
|
|
|
|
|
if str, ok := item.(string); ok && str != "" {
|
|
|
|
|
channel.Messages[i].ID = messageNumber
|
|
|
|
|
newText = append(newText, str)
|
|
|
|
|
messageNumber++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(newText) > 0 {
|
|
|
|
|
channel.Messages[i].Text = strings.Join(newText, " ")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-16 00:03:52 +03:00
|
|
|
|
|
2023-10-16 00:23:43 +03:00
|
|
|
|
cleanedMessages := make([]Message, 0)
|
|
|
|
|
for _, message := range channel.Messages {
|
|
|
|
|
if message.Text != "" {
|
|
|
|
|
cleanedMessages = append(cleanedMessages, message)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
channel.Messages = cleanedMessages
|
|
|
|
|
|
|
|
|
|
formattedJSON, err := json.MarshalIndent(channel, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Ошибка форматирования JSON:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(content, formattedJSON, 0644); err != nil {
|
|
|
|
|
fmt.Printf("Ошибка записи JSON файла %d: %d", content, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println("JSON файл успешно записан по пути", content)
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-10-16 00:03:52 +03:00
|
|
|
|
|
|
|
|
|
func loadMessages(filename string) error {
|
|
|
|
|
data, err := ioutil.ReadFile(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &messages); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxID = determineMaxID()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func determineMaxID() int {
|
|
|
|
|
max := 0
|
|
|
|
|
for _, message := range messages.Messages {
|
|
|
|
|
if message.ID > max {
|
|
|
|
|
max = message.ID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
idStr := r.URL.Path[1:]
|
|
|
|
|
|
|
|
|
|
id, err := strconv.Atoi(idStr)
|
|
|
|
|
|
|
|
|
|
if err != nil || id < 1 || id > maxID {
|
|
|
|
|
id = rand.Intn(maxID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
message := findMessageByID(id)
|
|
|
|
|
|
|
|
|
|
userAgent := r.UserAgent()
|
|
|
|
|
|
|
|
|
|
if strings.HasPrefix(userAgent, "curl") {
|
|
|
|
|
if text, ok := message.Text.(string); ok {
|
|
|
|
|
writePlainTextResponse(w, text, id)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Ошибка: неверный Анек")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
prevID, nextID := getPrevNextIDs(id)
|
|
|
|
|
htmlTemplate, _ := ioutil.ReadFile(html)
|
|
|
|
|
if text, ok := message.Text.(string); ok {
|
|
|
|
|
htmlContent := generateHTMLContent(w, htmlTemplate, id, text, prevID, nextID)
|
|
|
|
|
writeHTMLResponse(w, htmlContent)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Ошибка: неверный Анек")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findMessageByID(id int) *Message {
|
|
|
|
|
for _, message := range messages.Messages {
|
|
|
|
|
if message.ID == id {
|
|
|
|
|
return &message
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getPrevNextIDs(id int) (int, int) {
|
|
|
|
|
prevID := id - 1
|
|
|
|
|
if id <= 1 {
|
|
|
|
|
prevID = maxID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextID := id + 1
|
|
|
|
|
if id >= maxID {
|
|
|
|
|
nextID = 1
|
|
|
|
|
}
|
|
|
|
|
return prevID, nextID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func generateHTMLContent(w http.ResponseWriter, htmlTemplate []byte, id int, text string, prevID int, nextID int) string {
|
|
|
|
|
htmlContent := string(htmlTemplate)
|
|
|
|
|
htmlContent = strings.ReplaceAll(htmlContent, "CURRENT_ANEK", strconv.Itoa(id))
|
|
|
|
|
htmlContent = strings.ReplaceAll(htmlContent, "TEMPLATE_TEXT", text)
|
|
|
|
|
htmlContent = strings.ReplaceAll(htmlContent, "PREV_ANEK", strconv.Itoa(prevID))
|
|
|
|
|
htmlContent = strings.ReplaceAll(htmlContent, "NEXT_ANEK", strconv.Itoa(nextID))
|
|
|
|
|
return htmlContent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writePlainTextResponse(w http.ResponseWriter, text string, id int) {
|
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
|
w.Write([]byte("Анек №" + strconv.Itoa(id) + "\n\n" + text + "\n"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeHTMLResponse(w http.ResponseWriter, htmlContent string) {
|
|
|
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
|
w.Write([]byte(htmlContent))
|
|
|
|
|
}
|