أتمتة جمع القسيمة للأدب المجاني

خلفية


في لتر هناك نظام من المكافآت والقسائم التي تظهر مع انتظام يحسد عليه. لجعل زوجته سعيدة ، وبشكل عام ، يمكنه العثور على كتاب مثير للاهتمام لنفسه ، بدأ في مراقبة الموقع الذي تظهر فيه القسائم الجديدة وإسقاطها في برقيات. ولكن بعد أيام قليلة فقط سئمت من هذا العمل وقررت أتمتة هذه العملية بحيث تكون متاحة لكل من يريدها.


التنفيذ


نظرًا لأنني نشرت باستمرار قسائم جديدة في البرقيات ، وعمومًا أحب هذه الأداة ، فقد قررت إنشاء روبوت آخر للبرقيات ، طالما تم إنشاء عدد كاف من المكتبات له بالفعل. خذ golang و برقية-بوت المعهد مكتبة كلغة . نحتاج أيضًا إلى اختيار مورد لسحب المعلومات منه ، وكان لدي العديد من المواقع في ذهني واعتقدت أن أكتب محللًا عالميًا ككل ، ولكن في وقت ما أصبحت كسولًا ، وقررت اختيار مورد واحد. من أجل تخزين القسائم حتى بعد إعادة التشغيل ، قررت استخدام قاعدة بيانات sqlite3 بسيطة. سنقوم بتخزين معلومات حول القسائم الموجودة فيه ، بالإضافة إلى معلومات حول المستخدمين المسجلين في برنامج Telegram ، بالإضافة إلى معلومات حول القسائم التي تلقاها المستخدم بالفعل والتي لم يتم استلامها بعد.


يبدو شيء من هذا القبيل


صورة


تحليل الموقع


سيتم تحليل الموقع بواسطة مكتبة goquery - وهي تعمل بنفس الطريقة التي تعمل بها jquery.
باستخدام بنية goquery.Docent 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


 الآن زوجتي سعيدة لأنها تستطيع تلقي كتب مجانية على اللترات ، لكنني كنت مهتمًا فقط بحل هذه المشكلة. لا يزال هناك شيء يمكن تحسينه ، أضف نظام تحذير إذا فشل MarkAsRead ، (حتى الآن لم يحدث هذا ، لكنك لا تعرف أبدًا) ، كما أنه يقوم الآن بإلغاء الاشتراك ولم يعد يرسل رسائل إلى الأشخاص الذين قاموا بإلغاء الاشتراك منه وتحتاج إلى إعادتها إلى الحالة النشطة بعد الضغط على الأمر / start مرة أخرى. حسنًا ، بشكل عام ، أضف القدرة على اختيار وقت التوزيع واختيار القسائم ، لأن الموقع لا يحتوي فقط على قسائم من LiteRes. ولكن كل هذا ضروري ، حتى الآن لم يتم تلقي مثل هذه الطلبات.


المراجع


  1. المشروع نفسه
  2. موقع القسيمة
  3. اسم بوتxFreeCouponBot

All Articles