Automatisieren Sie die Couponsammlung für kostenlose Literatur

Hintergrund


Bei Litern gibt es ein System von Boni und Coupons, die mit beneidenswerter Regelmäßigkeit erscheinen. Um seine Frau glücklich zu machen und im Allgemeinen ein interessantes Buch für sich zu finden, begann er, die Stelle zu überwachen, an der neue Gutscheine erscheinen, und ließ sie in Telegrammen fallen. Aber nur ein paar Tage später hatte ich dieses Geschäft satt und beschloss, diesen Prozess zu automatisieren, damit er allen zur Verfügung steht, die ihn wollen.


Implementierung


Da ich ständig neue Gutscheine in Telegrammen gepostet habe und dieses Tool im Allgemeinen gefällt, habe ich beschlossen, einen weiteren Bot für Telegramme zu erstellen, sofern bereits eine ausreichende Anzahl von Bibliotheken dafür erstellt wurde. Nehmen Sie Golang und die Telegramm-Bot-API- Bibliothek als Sprache . Wir müssen auch eine Ressource auswählen, aus der Informationen abgerufen werden sollen. Ich hatte mehrere Websites im Sinn und dachte daran, einen universellen Parser als Ganzes zu schreiben, aber irgendwann wurde ich faul und entschied mich für eine Ressource. Um Gutscheine auch nach dem Neustart zu speichern, habe ich mich für eine einfache sqlite3-Datenbank entschieden. Wir speichern darin Informationen zu Gutscheinen sowie Informationen zu registrierten Benutzern im Telegramm-Bot sowie Informationen darüber, welche Gutscheine der Benutzer bereits erhalten hat und welche noch nicht.


Es sieht ungefähr so ​​aus


Bild


Site-Analyse


Das Parsen der Site wird von der goquery- Bibliothek durchgeführt - es funktioniert ähnlich wie jquery.
Verwenden der Struktur goquery.Document html . , , . . unixtime, . , , . , , js .


Telegram bot


BotFather , telegram . telegram api , http websocket. , telegram-bot-api updater .


type SNBot struct {
    cfg *Config
    bot *tgbotapi.BotAPI
    upd tgbotapi.UpdatesChannel
}

func New(cfg *Config) (*SNBot, error) {
    bot, err := tgbotapi.NewBotAPI(cfg.Token)
    if err != nil {
        return nil, err
    }
    level.Info(cfg.Logger).Log("msg", "Authorized on account", "bot-name", bot.Self.UserName)
    u := tgbotapi.NewUpdate(0)
    u.Timeout = cfg.UpdateTime
    updates, err := bot.GetUpdatesChan(u)
    if err != nil {
        return nil, err
    }
    return &SNBot{
        cfg: cfg,
        bot: bot,
        upd: updates,
    }, nil
}

, , gocron. task gocron storage storage .


Task function
func task(bot *snbot.SNBot, s *storage.Storage, c *collector.Collector, logger kitlog.Logger) {
    c.Collect(collector.ConditionQuery{
        URI: "https://lovikod.ru/knigi/promokody-litres",
    })
    chats, err := s.GetChat()
    if err != nil {
        level.Error(logger).Log("msg", "failed get chats", "err", err)
    }
    for _, id := range chats {
        records, err := s.GetNotUseCoupon(id)
        if err != nil {
            level.Error(logger).Log("msg", "failed get coupons", "err", err)
            return
        }
        var msg string
        for i, rec := range records {
            msg = fmt.Sprintf("%v%v:\t%s \n--->: %s\n : %v\n: %s\n\n", msg, i+1, rec.Link, rec.Code, time.Unix(rec.Date, 0).Format("02.01.2006"), rec.Description)
        }
        if len(msg) != 0 {
            err = bot.Send(id, msg)
            if err != nil {
                level.Error(logger).Log("msg", "failed send message", "err", err)
                continue
            }
            err = s.MarkAsRead(id, records)
            if err != nil {
                level.Error(logger).Log("msg", "failed marked as read", "err", err)
                continue
            }
        }
    }
    level.Info(logger).Log("msg", "send all chats new coupons")
}

- , , , .

func (s *SNBot) Send(chatID int64, msg string) error {
    level.Error(s.cfg.Logger).Log("msg", "try send", "chatID", chatID)
    var numericKeyboard = tgbotapi.NewReplyKeyboard(
        tgbotapi.NewKeyboardButtonRow(
            tgbotapi.NewKeyboardButton("/print5"),
        ),
    )
    m := tgbotapi.NewMessage(chatID, msg)
    m.ReplyMarkup = numericKeyboard
    _, err := s.bot.Send(m)
    if err != nil {
        if err.Error() == errBlockedByUser {
            s.cfg.Storage.UpdChatActivity(chatID, false)
        }
        return err
    }
    return nil
}

  Dockerfile


,   .


# build binary
FROM golang:1.10.3-alpine3.8 AS build
RUN apk add --no-cache linux-headers gcc g++
ARG VERSION=dev
WORKDIR /go/src/github.com/wenkaler/xfreehack
COPY . /go/src/github.com/wenkaler/xfreehack
RUN CGO_ENABLED=1 go build \
    -o /out/xfree \
    -ldflags "-X main.serviceVersion=$VERSION" \
    github.com/wenkaler/xfreehack/cmd

# copy to alpine image
FROM alpine:3.8
WORKDIR /app
RUN mkdir /db
COPY --from=build /out/xfree /app
RUN apk add --no-cache tzdata
RUN apk --no-cache add ca-certificates
ENV TZ Europe/Moscow
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
CMD ["/app/xfree"]

Systemd


. 2.6, upgrad- systemd. ? — 3.


[Unit]
Description=Xfree service
After=network.target
After=network-online.target

[Service]
ExecStart=/urs/local/bin/xfree
Environment="TELEGRAM_TOKEN=$TELEGRAM_TOKEN" "PATH_DB=/db/xfree.db"
TimeoutSec=30
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target


 Jetzt ist meine Frau froh, dass sie kostenlose Bücher über Liter erhalten kann, aber ich war nur daran interessiert, dieses Problem zu lösen. Es gibt noch etwas, das verbessert werden kann. Fügen Sie ein Warnsystem hinzu, wenn MarkAsRead fehlschlägt (bisher ist dies nicht geschehen, aber Sie wissen es nie). Außerdem meldet er sich jetzt ab und sendet keine Nachrichten mehr an Personen, die sich von ihm abgemeldet haben. Sie müssen sie nach erneutem Drücken des Befehls / start wieder in den aktiven Zustand versetzen. Fügen Sie im Allgemeinen die Möglichkeit hinzu, den Zeitpunkt der Verteilung und die Auswahl der Gutscheine zu wählen, da die Website nicht nur Gutscheine von LiteRes enthält. Dies ist jedoch alles notwendig, bisher sind keine derartigen Anträge eingegangen.


Verweise


  1. Projekt selbst
  2. Gutschein-Website
  3. Bot Name @xFreeCouponBot

All Articles