parent
19d4e2c27d
commit
a9aea7b3d9
|
@ -9,4 +9,7 @@ shedules
|
|||
*.env
|
||||
.vscode
|
||||
*.html
|
||||
*.pdf
|
||||
*.pdf
|
||||
help.txt
|
||||
main
|
||||
wkhtmltoimage
|
||||
|
|
25
main.go
25
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
@ -13,13 +14,18 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
ssau_parser.HeadURL = "http://127.0.0.1:5000/prod"
|
||||
ssau_parser.HeadURL = "https://ssau.ru"
|
||||
if err := tg.CheckEnv(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logs := database.OpenLogs()
|
||||
defer logs.CloseAll()
|
||||
//bot := new(tg.Bot)
|
||||
log.SetOutput(io.MultiWriter(os.Stderr, logs.ErrorFile))
|
||||
help, err := os.ReadFile("help.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// bot.Debug = log.New(io.MultiWriter(os.Stderr, database.CreateLog("messages")), "", log.LstdFlags)
|
||||
bot, err := tg.InitBot(
|
||||
logs,
|
||||
|
@ -37,7 +43,12 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bot.TestUser, err = strconv.ParseInt(os.Getenv("TELEGRAM_TEST_USER"), 0, 64)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bot.WkPath = os.Getenv("WK_PATH")
|
||||
bot.HelpTxt = string(help)
|
||||
//now, _ := time.Parse("2006-01-02 15:04 -07", "2023-02-07 07:00 +04")
|
||||
now := time.Now()
|
||||
next := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), (now.Minute() + 1), 0, 0, now.Location())
|
||||
|
@ -45,21 +56,20 @@ func main() {
|
|||
log.Println("Waiting...")
|
||||
time.Sleep(next.Sub(now))
|
||||
mailTicker := time.NewTicker(1 * time.Minute)
|
||||
sheduleTicker := time.NewTicker(1 * time.Minute)
|
||||
sheduleTicker := time.NewTicker(30 * time.Minute)
|
||||
log.Println("Started")
|
||||
defer mailTicker.Stop()
|
||||
defer sheduleTicker.Stop()
|
||||
for {
|
||||
select {
|
||||
case update := <-*bot.Updates:
|
||||
now = time.Now().Add(-6 * 30 * 24 * time.Hour)
|
||||
now = time.Now()
|
||||
_, err := bot.HandleUpdate(update, now)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
case <-mailTicker.C:
|
||||
now = time.Now().Add(-6 * 30 * 24 * time.Hour)
|
||||
//now = now.Add(5 * time.Minute)
|
||||
now = time.Now()
|
||||
log.Println(now)
|
||||
notes, err := notify.CheckNext(bot.DB, now)
|
||||
if err != nil {
|
||||
|
@ -68,9 +78,8 @@ func main() {
|
|||
notify.FirstMailing(bot, now)
|
||||
notify.Mailing(bot, notes, now)
|
||||
notify.ClearTemp(bot, now)
|
||||
//return
|
||||
case <-sheduleTicker.C:
|
||||
now = time.Now().Add(-6 * 30 * 24 * time.Hour)
|
||||
now = time.Now()
|
||||
if now.Hour() > 8 && now.Hour() < 20 {
|
||||
log.Println("check changes")
|
||||
notify.CheckShedules(bot, now)
|
||||
|
|
|
@ -21,6 +21,7 @@ type DB struct {
|
|||
|
||||
type LogFiles struct {
|
||||
DebugFile *os.File
|
||||
ErrorFile *os.File
|
||||
TgLogFile *os.File
|
||||
DBLogFile *os.File
|
||||
}
|
||||
|
@ -28,6 +29,7 @@ type LogFiles struct {
|
|||
func OpenLogs() (files LogFiles) {
|
||||
return LogFiles{
|
||||
DebugFile: CreateLog("messages"),
|
||||
ErrorFile: CreateLog("error"),
|
||||
TgLogFile: CreateLog("tg"),
|
||||
DBLogFile: CreateLog("sql"),
|
||||
}
|
||||
|
@ -35,6 +37,7 @@ func OpenLogs() (files LogFiles) {
|
|||
|
||||
func (files *LogFiles) CloseAll() {
|
||||
files.DebugFile.Close()
|
||||
files.ErrorFile.Close()
|
||||
files.TgLogFile.Close()
|
||||
files.DBLogFile.Close()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.l9labs.ru/anufriev.g.a/l9_stud_bot/modules/database"
|
||||
|
@ -85,7 +86,7 @@ func CheckNext(db *xorm.Engine, now time.Time) ([]Notify, error) {
|
|||
var next_lesson database.Lesson
|
||||
if _, err := db.
|
||||
Where(
|
||||
"groupid = ? and begin >= ?",
|
||||
"groupid = ? and begin > ?",
|
||||
l.GroupId, l.Begin.Format("2006-01-02 15:04:05"),
|
||||
).
|
||||
Asc("begin").
|
||||
|
@ -219,9 +220,18 @@ func Mailing(bot *tg.Bot, notes []Notify, now time.Time) {
|
|||
msg.ParseMode = tgbotapi.ModeHTML
|
||||
m, err := bot.TG.Send(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
// Удаление пользователя, заблокировавшего бота
|
||||
if !strings.Contains(err.Error(), "blocked by user") {
|
||||
bot.DB.Delete(&user)
|
||||
bot.DB.Delete(&database.ShedulesInUser{L9Id: user.L9Id})
|
||||
bot.DB.Delete(&database.User{L9Id: user.L9Id})
|
||||
continue
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
} else {
|
||||
AddTemp(m, tempTime, bot)
|
||||
}
|
||||
AddTemp(m, tempTime, bot)
|
||||
} else {
|
||||
if err = bot.GetWeekSummary(
|
||||
note.Lesson.Begin,
|
||||
|
@ -229,9 +239,10 @@ func Mailing(bot *tg.Bot, notes []Notify, now time.Time) {
|
|||
database.ShedulesInUser{},
|
||||
0,
|
||||
true,
|
||||
"На этой неделе больше ничего нет\n\nВыше расписание на следующую неделю",
|
||||
"На этой неделе больше ничего нет\n\nНа фото расписание на следующую неделю",
|
||||
); err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
ids = append(ids, user.TgId)
|
||||
|
@ -271,7 +282,7 @@ func ClearTemp(bot *tg.Bot, now time.Time) {
|
|||
|
||||
var firstMailQuery = `SELECT t.tgId, l.lessonId, u.firsttime
|
||||
FROM ShedulesInUser u
|
||||
JOIN (SELECT lessonid, groupid, type, min(begin) as begin FROM Lesson WHERE date(begin) = date('%s') GROUP BY lessonid, groupid, type, begin) l
|
||||
JOIN (SELECT lessonid, groupid, type, min(begin) as begin FROM Lesson WHERE date(begin) = date('%s') GROUP BY groupid) l
|
||||
ON '%s' = DATE_SUB(l.Begin, INTERVAL u.firsttime MINUTE) AND u.sheduleid = l.groupid
|
||||
JOIN TgUser t ON u.L9ID = t.L9ID
|
||||
WHERE u.first = true AND (l.type != "mil" OR (l.type = "mil" AND u.military = true));`
|
||||
|
@ -309,6 +320,7 @@ func FirstMailing(bot *tg.Bot, now time.Time) {
|
|||
msg, err := bot.TG.Send(mail)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
AddTemp(msg, lesson.Begin.Add(15*time.Minute), bot)
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ func (sh *WeekShedule) Parse(p Page, uncover bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var types = [4]string{"lect", "lab", "pract", "other"}
|
||||
var types = []string{"lect", "lab", "pract", "other", "exam", "cons", "kurs"}
|
||||
|
||||
// Парсинг занятия
|
||||
func ParseLesson(s *goquery.Selection, isGroup bool, sheduleId int64) []Lesson {
|
||||
|
|
|
@ -16,13 +16,14 @@ import (
|
|||
)
|
||||
|
||||
type Bot struct {
|
||||
TG *tgbotapi.BotAPI
|
||||
DB *xorm.Engine
|
||||
// TG_user database.TgUser
|
||||
Week int
|
||||
WkPath string
|
||||
Debug *log.Logger
|
||||
Updates *tgbotapi.UpdatesChannel
|
||||
TG *tgbotapi.BotAPI
|
||||
DB *xorm.Engine
|
||||
TestUser int64
|
||||
HelpTxt string
|
||||
Week int
|
||||
WkPath string
|
||||
Debug *log.Logger
|
||||
Updates *tgbotapi.UpdatesChannel
|
||||
}
|
||||
|
||||
var env_keys = []string{
|
||||
|
@ -131,6 +132,10 @@ func (bot *Bot) HandleUpdate(update tgbotapi.Update, now ...time.Time) (tgbotapi
|
|||
return nilMsg, err
|
||||
}
|
||||
bot.Debug.Printf("Message [%d] <%s> %s", user.L9Id, user.Name, msg.Text)
|
||||
if strings.Contains(msg.Text, "/help") {
|
||||
msg := tgbotapi.NewMessage(user.TgId, bot.HelpTxt)
|
||||
return bot.TG.Send(msg)
|
||||
}
|
||||
switch user.PosTag {
|
||||
case database.NotStarted:
|
||||
err = bot.Start(user)
|
||||
|
@ -142,6 +147,36 @@ func (bot *Bot) HandleUpdate(update tgbotapi.Update, now ...time.Time) (tgbotapi
|
|||
return bot.GetPersonal(now[0], user)
|
||||
} else if msg.Text == "Настройки" {
|
||||
return bot.GetOptions(user)
|
||||
} else if strings.Contains(msg.Text, "/keyboard") {
|
||||
options := database.ShedulesInUser{
|
||||
L9Id: user.L9Id,
|
||||
}
|
||||
if _, err := bot.DB.Get(&options); err != nil {
|
||||
return nilMsg, err
|
||||
}
|
||||
msg := tgbotapi.NewMessage(user.TgId, "Клавиатура выдана")
|
||||
msg.ReplyMarkup = GeneralKeyboard(options.UID != 0)
|
||||
return bot.TG.Send(msg)
|
||||
} else if strings.Contains(msg.Text, "/scream") && user.TgId == bot.TestUser {
|
||||
var users []database.TgUser
|
||||
if err := bot.DB.Where("tgid > 0").Find(&users); err != nil {
|
||||
return nilMsg, err
|
||||
}
|
||||
msg := tgbotapi.NewMessage(
|
||||
0,
|
||||
strings.TrimPrefix(msg.Text, "/scream"),
|
||||
)
|
||||
for _, u := range users {
|
||||
msg.ChatID = u.TgId
|
||||
if _, err := bot.TG.Send(msg); err != nil {
|
||||
if !strings.Contains(err.Error(), "blocked by user") {
|
||||
bot.Debug.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.ChatID = bot.TestUser
|
||||
msg.Text = "Сообщения отправлены"
|
||||
return bot.TG.Send(msg)
|
||||
}
|
||||
return bot.Find(now[0], user, msg.Text)
|
||||
case database.Add:
|
||||
|
@ -210,6 +245,13 @@ func (bot *Bot) DeleteGroup(user *database.TgUser, text string) (tgbotapi.Messag
|
|||
if _, err := bot.DB.Delete(&userInfo); err != nil {
|
||||
return nilMsg, err
|
||||
}
|
||||
files := database.File{
|
||||
TgId: user.L9Id,
|
||||
IsPersonal: true,
|
||||
}
|
||||
if _, err := bot.DB.UseBool("IsPersonal").Delete(&files); err != nil {
|
||||
return nilMsg, err
|
||||
}
|
||||
msg = tgbotapi.NewMessage(user.TgId, "Группа отключена")
|
||||
msg.ReplyMarkup = GeneralKeyboard(false)
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,12 @@ func (bot *Bot) Start(user *database.TgUser) error {
|
|||
msg := tgbotapi.NewMessage(
|
||||
user.TgId,
|
||||
`Привет! У меня можно посмотреть в удобном формате <b>ближайшие пары</b>, расписание <b>по дням</b> и даже <b>по неделям</b>!
|
||||
Просто напиши мне <b>номер группы</b> или <b>фамилию преподавателя</b>`)
|
||||
Просто напиши мне <b>номер группы</b> или <b>фамилию преподавателя</b>
|
||||
|
||||
Также можно получать уведомления о своих занятиях по кнопке <b>Моё расписание</b>👇
|
||||
|
||||
‼ Внимание! Бот ещё находится на стадии испытаний, поэтому могут возникать ошибки в его работе.
|
||||
Рекомендуется сверять настоящее расписание и обо всех ошибках сообщать по контакам в /help`)
|
||||
msg.ParseMode = tgbotapi.ModeHTML
|
||||
msg.ReplyMarkup = GeneralKeyboard(false)
|
||||
_, err = bot.TG.Send(msg)
|
||||
|
@ -135,7 +140,9 @@ func (bot *Bot) Find(now time.Time, user *database.TgUser, query string) (tgbota
|
|||
msg := tgbotapi.NewMessage(
|
||||
user.TgId,
|
||||
"Расписание успешно подключено!\n"+
|
||||
"Теперь его можно открыть по кнопке <b>Моё расписание</b>👇",
|
||||
"Теперь можно смотреть свои занятия по кнопке <b>Моё расписание</b>👇\n\n"+
|
||||
"Также ты будешь получать уведомления о занятиях, "+
|
||||
"которыми можно управлять в панели <b>Настройки</b>\n",
|
||||
)
|
||||
msg.ParseMode = tgbotapi.ModeHTML
|
||||
msg.ReplyMarkup = GeneralKeyboard(true)
|
||||
|
@ -234,7 +241,7 @@ func (bot *Bot) GetShedule(user *database.TgUser, query *tgbotapi.CallbackQuery,
|
|||
if _, err = bot.DB.ID(user.L9Id).Update(user); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bot.GetPersonal(now[0], user)
|
||||
_, err = bot.GetPersonal(now[0], user, *query.Message)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ func (bot *Bot) GetOptions(user *database.TgUser) (tgbotapi.Message, error) {
|
|||
if _, err := bot.DB.Get(&options); err != nil {
|
||||
return nilMsg, err
|
||||
}
|
||||
// Если кто-то хитрожопый нажал на кнопку без подключенной группы
|
||||
if options.UID == 0 {
|
||||
msg := tgbotapi.NewMessage(user.TgId, "У тебя пока не подключено ни одной группы\nНажми на кнопку <b>Моё расписание</b>")
|
||||
msg.ParseMode = tgbotapi.ModeHTML
|
||||
msg.ReplyMarkup = GeneralKeyboard(false)
|
||||
return bot.TG.Send(msg)
|
||||
}
|
||||
markup := OptMarkup(options)
|
||||
msg := tgbotapi.NewMessage(user.TgId, optStr)
|
||||
msg.ReplyMarkup = markup
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
func (bot *Bot) GetPersonal(now time.Time, user *database.TgUser, editMsg ...tgbotapi.Message) (tgbotapi.Message, error) {
|
||||
var shedules []database.ShedulesInUser
|
||||
bot.DB.ID(user.L9Id).Find(&shedules)
|
||||
bot.DB.Where("l9id = ?", user.L9Id).Find(&shedules)
|
||||
|
||||
if len(shedules) == 0 {
|
||||
user.PosTag = database.Add
|
||||
|
@ -131,8 +131,12 @@ func (bot *Bot) GetSummary(
|
|||
return bot.EditOrSend(user.TgId, str, "", markup, editMsg...)
|
||||
|
||||
} else {
|
||||
msg := tgbotapi.NewMessage(user.TgId, "Ой! Занятий не обнаружено ):")
|
||||
return bot.TG.Send(msg)
|
||||
return bot.EditOrSend(
|
||||
user.TgId,
|
||||
"Ой! Занятий не обнаружено ):",
|
||||
"",
|
||||
tgbotapi.InlineKeyboardMarkup{},
|
||||
editMsg...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,7 +323,17 @@ func GroupPairs(lessons []database.Lesson) [][]database.Lesson {
|
|||
return shedule
|
||||
}
|
||||
|
||||
var Icons = map[string]string{"lect": "📗", "pract": "📕", "lab": "📘", "other": "📙", "mil": "🫡", "window": "🏝"}
|
||||
var Icons = map[string]string{
|
||||
"lect": "📗 Лекция ",
|
||||
"pract": "📕 Практика ",
|
||||
"lab": "📘 Лаба ",
|
||||
"other": "📙 Прочее ",
|
||||
"mil": "🫡",
|
||||
"window": "🏝",
|
||||
"exam": "💀 Экзамен",
|
||||
"cons": "🗨 Консультация",
|
||||
"kurs": "🤯 Курсовая",
|
||||
}
|
||||
|
||||
// Конвертация занятий с текст
|
||||
func PairToStr(pair []database.Lesson, db *xorm.Engine, isGroup bool) (string, error) {
|
||||
|
|
|
@ -140,6 +140,7 @@ func GenerateButtonTail(sheduleId int64, dt int, isGroup bool) string {
|
|||
}
|
||||
|
||||
// Отправка сообщения или его редактирование, если в editMsg указано сообщение
|
||||
// TODO: Обрабатывать старые сообщения, которые уже нельзя редактировать (message can't be deleted for everyone)
|
||||
func (bot *Bot) EditOrSend(
|
||||
id int64,
|
||||
str string,
|
||||
|
@ -229,6 +230,8 @@ func (bot *Bot) EditOrSend(
|
|||
msg := tgbotapi.NewMessage(id, str)
|
||||
if len(markup.InlineKeyboard) != 0 {
|
||||
msg.ReplyMarkup = &markup
|
||||
} else {
|
||||
msg.ReplyMarkup = GeneralKeyboard(false)
|
||||
}
|
||||
msg.ParseMode = tgbotapi.ModeHTML
|
||||
return bot.TG.Send(msg)
|
||||
|
|
|
@ -245,14 +245,14 @@ func (bot *Bot) CreateWeekImg(
|
|||
}
|
||||
|
||||
input := fmt.Sprintf("./%s/week_%d.html", path, week-bot.Week)
|
||||
output := fmt.Sprintf("./%s/week_%d.png", path, week-bot.Week)
|
||||
output := fmt.Sprintf("./%s/week_%d.jpg", path, week-bot.Week)
|
||||
f, _ := os.Create(input)
|
||||
defer f.Close()
|
||||
f.WriteString(html)
|
||||
|
||||
cmd := exec.CommandContext(context.Background(), bot.WkPath, []string{
|
||||
"--width",
|
||||
"1280",
|
||||
"1600",
|
||||
input,
|
||||
output,
|
||||
}...)
|
||||
|
@ -339,7 +339,7 @@ const head = `<html lang="ru">
|
|||
</head>
|
||||
|
||||
<style>
|
||||
.note div,.rasp div{background-color:#f0f8ff;padding:10px;text-align:center;border-radius:10px}.note,th.head,th.time{font-family:monospace}.subj div #text,.subj p{display:none}html{font-size:1.5rem}body{background:#dc14bd}table{table-layout:fixed;width:100%;border-spacing:5px 5px}.note div{margin:10px 0}.head p,.subj p,hr{margin:0}.rasp div{transition:.3s}th.head{background-color:#0ff;border-radius:10px;padding:5px;font-size:1.05rem}th.subj,th.time{background-color:#f0f8ff;padding:10px;border-radius:10px}th.time{font-size:1.1rem}.subj h2,.subj p{font-size:.85rem}th.subj:not(.lab,.lect,.pract,.other){background-color:#a9a9a9}.subj div{border-radius:10px;padding:5px}.subj p{font-family:monospace;color:#f0f8ff}.subj h2,.subj h3,.subj h5{font-family:monospace;text-align:left;margin:5px}.subj h3{font-size:.65rem}.subj h5{font-size:.7rem;font-weight:400}.lect div{background-color:#7fff00}.pract div{background-color:#dc143c}.lab div{background-color:#8a2be2}.other div{background-color:#ff8c00}.mil div{background-color:#ff8c00}.window div{background-color:blue}
|
||||
.subj div,th.head,th.subj,th.time{border-radius:10px}.note,.subj p,th.head,th.time{font-family:monospace}.note div,.rasp div{background-color:#f0f8ff;padding:10px;text-align:center;border-radius:10px}.subj div #text,.subj p{display:none}html{font-size:1.5rem}body{background:#dc14bd}table{table-layout:fixed;width:100%;border-spacing:5px 5px}.note div{margin:10px 0}.head p,.subj p,hr{margin:0}.rasp div{transition:.3s}th.head{background-color:#0ff;padding:5px;font-size:1.05rem}th.subj,th.time{background-color:#f0f8ff;padding:10px}th.time{font-size:1.1rem}.subj h2,.subj p{font-size:.85rem}th.subj:not(.lab,.lect,.pract,.other){background-color:#a9a9a9}.subj div{padding:5px}.subj p{color:#f0f8ff}.subj h2,.subj h3,.subj h5{font-family:monospace;text-align:left;margin:5px}.subj h3{font-size:.65rem}.subj h5{font-size:.7rem;font-weight:400}.lect div{background-color:#7fff00}.pract div{background-color:#dc143c}.lab div{background-color:#8a2be2}.mil div,.other div{background-color:#ff8c00}.window div{background-color:#00f}.cons div{background-color:green}.exam div{background-color:purple}.kurs div{background-color:orange}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
|
Reference in New Issue