Automatisez la collecte des coupons pour la documentation gratuite

Contexte


Litres a un système de bonus et de coupons qui apparaissent avec une régularité enviable. Pour rendre sa femme heureuse, et en général, il peut trouver un livre intéressant pour lui-même, il a commencé à surveiller le site dans lequel de nouveaux coupons apparaissent et les a déposés dans des télégrammes. Mais quelques jours plus tard, j'étais fatigué de cette activité et j'ai décidé d'automatiser ce processus afin qu'il soit accessible à tous ceux qui le souhaitent.


la mise en oeuvre


Comme je publie constamment de nouveaux coupons dans les télégrammes, et en général j'aime cet outil, j'ai décidé de créer un autre bot pour les télégrammes, tant qu'un nombre suffisant de bibliothèques ont déjà été créées pour lui. Prenez golang et la bibliothèque telegram-bot-api comme langue . Nous devons également choisir une ressource à partir de laquelle extraire des informations, j'avais plusieurs sites en tête et j'ai pensé écrire un analyseur universel dans son ensemble, mais à un moment donné, je suis devenu paresseux et j'ai décidé de choisir une ressource. Afin de stocker les coupons même après le redémarrage, j'ai décidé d'utiliser une simple base de données sqlite3. Nous y conserverons des informations sur les coupons, ainsi que des informations sur les utilisateurs enregistrés dans le télégramme, ainsi que des informations sur les coupons que l'utilisateur a déjà reçus et ceux qui ne le sont pas encore.


Cela ressemble à ceci


image


Analyse du site


L'analyse du site sera effectuée par la bibliothèque goquery - cela fonctionne de la même manière que jquery.
Utilisation de la structure 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


 Maintenant, ma femme est heureuse qu'elle puisse recevoir des livres gratuits sur les litres, mais je voulais simplement résoudre ce problème. Il y a encore quelque chose qui peut être amélioré, ajoutez un système d'avertissement si MarkAsRead échoue, (jusqu'à présent, cela ne s'est pas produit, mais vous ne savez jamais) , il se désabonne également et n'envoie plus de messages aux personnes qui se sont désabonnées de lui et vous devez les remettre à l'état actif après avoir appuyé à nouveau sur la commande / start. Eh bien, en général, ajoutez la possibilité de choisir l'heure de distribution et le choix des coupons, car le site n'a pas seulement des coupons de LiteRes. Mais tout cela est nécessaire, jusqu'à présent aucune demande de ce type n'a été reçue.


Références


  1. Se projeter
  2. Site Web des coupons
  3. Nom du bot @xFreeCouponBot

All Articles