go-watch/main.go

310 lines
7.6 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"path/filepath"
"strconv"
"text/template"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/spf13/viper"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var baseHTML = filepath.Join("templates", "base.html")
var indexHTML = filepath.Join("templates", "index.html")
var newWatchHTML = filepath.Join("templates", "newWatch.html")
type Web struct {
//Bot *tgbotapi.BotAPI
db *gorm.DB
}
func (web Web) index(c *gin.Context) {
//msg := tgbotapi.NewMessage(viper.GetInt64("telegram.chat"), message)
//web.Bot.Send(msg)
watches := []Watch{}
web.db.Find(&watches)
c.HTML(http.StatusOK, "index", watches)
}
func (web Web) canvas(c *gin.Context) {
c.HTML(http.StatusOK, "canvas", gin.H{})
}
func (web Web) watchCreate(c *gin.Context) {
c.HTML(http.StatusOK, "watchCreate", gin.H{})
}
func (web Web) watchCreatePost(c *gin.Context) {
log.Println("TEST")
var watch Watch
errMap, err := bindAndValidateWatch(&watch, c)
if err != nil {
c.HTML(http.StatusBadRequest, "500", errMap)
return
}
web.db.Create(&watch)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/%d", watch.ID))
}
func (web Web) deleteWatch(c *gin.Context) {
id, err := strconv.Atoi(c.PostForm("id"))
if err != nil {
c.Redirect(http.StatusSeeOther, "/watch/new")
return
}
web.db.Delete(&Watch{}, id)
c.Redirect(http.StatusSeeOther, "/")
}
func (web Web) watchView(c *gin.Context) {
id := c.Param("id")
var watch Watch
web.db.Model(&Watch{}).First(&watch, id)
var filters []Filter
web.db.Model(&Filter{}).Where("watch_id = ?", watch.ID).Find(&filters)
var connections []FilterConnection
web.db.Model(&FilterConnection{}).Where("watch_id = ?", watch.ID).Find(&connections)
c.HTML(http.StatusOK, "watch", gin.H{
"Watch": watch,
"Filters": filters,
"Connections": connections,
})
}
func (web Web) watchSave(c *gin.Context) {
var watch Watch
bindAndValidateWatch(&watch, c)
web.db.Save(&watch)
var newFilters []Filter
var filtersJson = c.PostForm("filters")
if err := json.Unmarshal([]byte(filtersJson), &newFilters); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
var newConnections []FilterConnection
var connectionsJson = c.PostForm("connections")
if err := json.Unmarshal([]byte(connectionsJson), &newConnections); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
var oldFilters []Filter
web.db.Model(&Filter{}).Where("watch_id = ?", watch.ID).Find(&oldFilters)
filterMap := make(map[uint]*Filter)
for i := range newFilters {
filter := &newFilters[i]
filterMap[filter.ID] = filter
filter.ID = 0
}
web.db.Delete(&Filter{}, "watch_id = ?", watch.ID)
if len(newFilters) > 0 {
web.db.Create(&newFilters)
}
web.db.Delete(&FilterConnection{}, "watch_id = ?", watch.ID)
log.Println(len(filterMap))
for i := range newConnections {
connection := &newConnections[i]
connection.OutputID = filterMap[connection.OutputID].ID
connection.InputID = filterMap[connection.InputID].ID
}
if len(newConnections) > 0 {
web.db.Create(&newConnections)
}
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/%d", watch.ID))
}
/*
func (web Web) viewWatch(c *gin.Context) {
id := c.Param("id")
var watch Watch
web.db.Model(&Watch{}).First(&watch, id)
var filters []Filter
web.db.Model(&Filter{}).Where("watch_id = ?", watch.ID).Find(&filters)
filterMap := make(map[uint]*Filter)
for i := range filters {
filterMap[filters[i].ID] = &filters[i]
}
currentLayerFilters := []*Filter{}
for i := range filters {
filter := &filters[i]
if filter.ParentID != nil {
parent := filterMap[*filter.ParentID]
parent.Filters = append(parent.Filters, *filter)
} else {
currentLayerFilters = append(currentLayerFilters, filter)
}
}
bftFilters := []FilterDepth{}
nextLayerFilters := []*Filter{}
depth := 0
for len(nextLayerFilters) > 0 || len(currentLayerFilters) > 0 {
for len(currentLayerFilters) > 0 {
filter := currentLayerFilters[0]
bftFilters = append(bftFilters, FilterDepth{
Filter: filter,
Depth: make([]struct{}, depth),
})
for _, filter := range filter.Filters {
nextLayerFilters = append(nextLayerFilters, &filter)
}
currentLayerFilters = currentLayerFilters[1:]
}
depth += 1
currentLayerFilters = nextLayerFilters
nextLayerFilters = []*Filter{}
}
for i := range bftFilters {
fd := &bftFilters[i]
fd.RevDepth = make([]struct{}, depth-len(fd.Depth))
}
numberOfColumns := depth + 4
c.HTML(http.StatusOK, "viewWatch", gin.H{
"Watch": watch,
"Filters": bftFilters,
"MaxDepth": depth,
"Columns": make([]struct{}, numberOfColumns),
"ColumnWidth": 100 / numberOfColumns,
})
}
*/
func passiveBot(bot *tgbotapi.BotAPI) {
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates := bot.GetUpdatesChan(u)
for update := range updates {
if update.Message != nil { // If we got a message
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
msg.ReplyToMessageID = update.Message.MessageID
bot.Send(msg)
}
}
}
func render(w http.ResponseWriter, filename string, data interface{}) {
tmpl, err := template.ParseFiles(filename, baseHTML)
if err != nil {
log.Print(err)
http.Error(w, "Something went wrong", http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, data); err != nil {
log.Print(err)
http.Error(w, "Something went wrong", http.StatusInternalServerError)
}
}
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
err := viper.ReadInConfig()
if err != nil {
log.Fatalln("Could not load config file")
}
db, _ := gorm.Open(sqlite.Open(viper.GetString("database.dsn")))
db.AutoMigrate(&Watch{}, &Filter{}, &FilterConnection{})
/*
watch := Watch{
Name: "LG C2 42",
Interval: 60,
}
db.Create(&watch)
urlFilter := Filter{
WatchID: watch.ID,
Name: "PriceWatch Fetch",
X: 100,
Y: 100,
Type: "url",
Var1: "https://tweakers.net/pricewatch/1799060/lg-c2-42-inch-donkerzilveren-voet-zwart.html",
}
db.Create(&urlFilter)
xpathFilter := Filter{
WatchID: watch.ID,
Name: "price select",
X: 300,
Y: 300,
Type: "xpath",
Var1: "//td[@class='shop-price']",
}
db.Create(&xpathFilter)
connection := FilterConnection{
WatchID: watch.ID,
OutputID: urlFilter.ID,
InputID: xpathFilter.ID,
}
db.Create(&connection)
*/
//bot, _ := tgbotapi.NewBotAPI(viper.GetString("telegram.token"))
//bot.Debug = true
//log.Printf("Authorized on account %s", bot.Self.UserName)
//go passiveBot(bot)
web := Web{
//bot,
db,
}
router := gin.Default()
router.Static("/static", "./static")
templates := multitemplate.NewRenderer()
templates.AddFromFiles("index", "templates/base.html", "templates/index.html")
templates.AddFromFiles("watchCreate", "templates/base.html", "templates/watchCreate.html")
templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html")
templates.AddFromFiles("editGroup", "templates/base.html", "templates/editGroup.html")
templates.AddFromFiles("watch", "templates/base.html", "templates/diagrams.html")
templates.AddFromFiles("500", "templates/base.html", "templates/500.html")
router.HTMLRender = templates
router.GET("/", web.index)
router.GET("/watch/new", web.watchCreate)
router.POST("/watch/create", web.watchCreatePost)
router.POST("/watch/delete", web.deleteWatch)
router.GET("/watch/:id", web.watchView)
router.POST("/watch/save", web.watchSave)
router.Run("0.0.0.0:8080")
}