背景
在升时,存在以令人羡慕的规律性出现的奖金和优惠券系统。为了使妻子高兴,总的来说,他可以为自己找到一本有趣的书,他开始监视出现新优惠券的地点,然后将其放入电报中。但是几天后,我对这项业务感到厌倦,因此决定自动执行此过程,以便任何需要它的人都可以使用。
实作
由于我不断在电报中发布新的优惠券,并且总的来说,我喜欢这个工具,因此,我决定创建另一个电报自动程序,只要已经为其创建了足够的库即可。以golang和telegram-bot-api库作为语言。我们还需要选择一种从中获取信息的资源,我想到了几个站点,并考虑编写一个整体的通用解析器,但是到了某个时候,我变得很懒惰,因此我决定选择一种资源。为了即使重新启动后也可以存储优惠券,我决定使用一个简单的sqlite3数据库。我们将在其中存储有关优惠券的信息,以及在电报bot中有关注册用户的信息,以及有关用户已经收到和尚未收到哪些优惠券的信息。
看起来像这样

网站解析
站点的解析将由goquery库完成-它的工作方式与jquery大致相同。
使用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 functionfunc 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
现在,我的妻子很高兴她可以收到有关LitRes的免费书籍,但我只是想解决这个问题,还有一些可以改进的地方,如果MarkAsRead失败,请添加警告系统(到目前为止,这还没有发生,但您永远不会知道) ,他现在也取消了订阅,不再向未订阅他的人发送消息,您需要在再次按/ start命令后将其恢复为活动状态。好吧,总的来说,可以添加选择分发时间和优惠券的功能,因为该站点不仅具有LiteRes的优惠券。但这一切都是必要的,到目前为止尚未收到此类申请。
参考文献
- 项目本身
- 优惠券网站
- 机器人名称@xFreeCouponBot