add conversions manager;
This commit is contained in:
parent
d4e42a57f5
commit
5806eb37c0
|
|
@ -1,5 +1,5 @@
|
|||
FROM golang:alpine as app-builder
|
||||
WORKDIR $GOPATH/src/ticket-creator/
|
||||
WORKDIR $GOPATH/src/ticket-pimp/
|
||||
COPY . .
|
||||
RUN apk add git
|
||||
# Static build required so that we can safely copy the binary over.
|
||||
|
|
@ -8,10 +8,10 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install -ldflags '-extldflags "-sta
|
|||
|
||||
FROM scratch
|
||||
# the test program:
|
||||
COPY --from=app-builder /go/bin/main /ticket-creator
|
||||
COPY --from=app-builder /go/src/ticket-creator/cmd/.env /
|
||||
COPY --from=app-builder /go/bin/main /ticket-pimp
|
||||
COPY --from=app-builder /go/src/ticket-pimp/cmd/.env /
|
||||
# the tls certificates:
|
||||
# NB: this pulls directly from the upstream image, which already has ca-certificates:
|
||||
COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ENTRYPOINT ["/ticket-creator"]
|
||||
ENTRYPOINT ["/ticket-pimp"]
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"io"
|
||||
d "ticket-pimp/bot/domain"
|
||||
"ticket-pimp/bot/ext"
|
||||
)
|
||||
|
||||
type WorkflowController struct {
|
||||
iGit ext.IGit
|
||||
iCloud ext.ICloud
|
||||
iYouTrack ext.IYouTrack
|
||||
iCoda ext.ICoda
|
||||
}
|
||||
|
||||
func NewWorkflowController(
|
||||
gitBaseURL,
|
||||
gitToken,
|
||||
cloudBaseURL,
|
||||
cloudAuthUser,
|
||||
cloudAuthPass,
|
||||
ytBaseURL,
|
||||
ytToken string,
|
||||
) *WorkflowController {
|
||||
return &WorkflowController{
|
||||
iGit: ext.NewGit(gitBaseURL, gitToken),
|
||||
iCloud: ext.NewCloud(cloudBaseURL, cloudAuthUser, cloudAuthPass),
|
||||
iYouTrack: ext.NewYT(ytBaseURL, ytToken),
|
||||
iCoda: ext.NewCodaClient(),
|
||||
}
|
||||
}
|
||||
|
||||
type IWorkflowController interface {
|
||||
Workflow(name string) (string, error)
|
||||
CreateRepo(name string) (*d.Git, error)
|
||||
CreateFolder(name string) (*d.Folder, error)
|
||||
|
||||
NewTask(summ, desc, c, cLink string) *Task
|
||||
CreateTask(t *Task) (*Task, error)
|
||||
|
||||
ThrowConversions(f io.ReadCloser, appID string, token string) *d.ConversionLog
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
d "ticket-pimp/bot/domain"
|
||||
)
|
||||
|
||||
func (wc *WorkflowController) ThrowConversions(f io.ReadCloser, appID string, token string) *d.ConversionLog {
|
||||
c := req.C().
|
||||
SetBaseURL("https://graph.facebook.com/v15.0/").
|
||||
DevMode()
|
||||
|
||||
const currency = "USD"
|
||||
|
||||
r := csv.NewReader(f)
|
||||
|
||||
conversionLog := d.ConversionLog{}
|
||||
|
||||
for {
|
||||
record, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
advertiser := strings.Split(record[0], ";")[0]
|
||||
|
||||
params := map[string]string{
|
||||
"advertiser_id": advertiser,
|
||||
"event": "CUSTOM_APP_EVENTS",
|
||||
"application_tracking_enabled": "1",
|
||||
"advertiser_tracking_enabled": "1",
|
||||
"custom_events": `[{"_eventName":"fb_mobile_purchase"}]`,
|
||||
}
|
||||
|
||||
res, _ := c.R().
|
||||
SetQueryString(token).
|
||||
SetQueryParams(params).
|
||||
Post(appID + "/activities")
|
||||
|
||||
if res.Err != nil {
|
||||
conversionLog.Advertiser = append(conversionLog.Advertiser, advertiser)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &conversionLog
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
function myFunction() {
|
||||
var sheet = SpreadsheetApp.getActiveSheet();
|
||||
var data = sheet.getDataRange().getValues();
|
||||
var a1 = data[1][1];
|
||||
var a2 = ''
|
||||
var a3 = data[1][2];
|
||||
var reqUrl = data[1][3];
|
||||
var startarray = data[1][4]
|
||||
|
||||
var regCount = 0;
|
||||
var depCount = 0;
|
||||
// reqUrl = 'https://graph.facebook.com/v15.0/841990173617973/activities?event=CUSTOM_APP_EVENTS&application_tracking_enabled=1&advertiser_tracking_enabled=1&advertiser_id=0a42bbee-c049-4180-ae3f-ce76ff33556e&841990173617973%7Cnjqit85G_uma0voikeP6eJvBiQk&custom_events=[%7B%22_eventName%22:%22fb_mobile_purchase%22%7D]'
|
||||
reqUrl = reqUrl.replace('A1', a1)
|
||||
reqUrl = reqUrl.replace('A3', a3)
|
||||
Logger.log("urll " + reqUrl)
|
||||
Logger.log("urll " + encodeURI(reqUrl).toString())
|
||||
|
||||
for (var i = startarray; i < data.length; i++) {
|
||||
a2 = data[i][0]
|
||||
reqUrl = reqUrl.replace('A2', a2)
|
||||
|
||||
var params = {
|
||||
"method": 'post'
|
||||
}
|
||||
var response = UrlFetchApp.fetch(encodeURI(reqUrl), params);
|
||||
Logger.log(i + " " + response.getContentText() + ' ' + response.getResponseCode());
|
||||
depCount++;
|
||||
|
||||
}
|
||||
Logger.log('Reg Count= ' + regCount);
|
||||
Logger.log('Dep Count= ' + depCount);
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package controller
|
||||
|
||||
import d "ticket-pimp/domain"
|
||||
import d "ticket-pimp/bot/domain"
|
||||
|
||||
func (wc *WorkflowController) CreateFolder(name string) (*d.Folder, error) {
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package controller
|
||||
|
||||
import d "ticket-pimp/domain"
|
||||
import d "ticket-pimp/bot/domain"
|
||||
|
||||
func (wc *WorkflowController) CreateRepo(name string) (*d.Git, error) {
|
||||
//Create git repository with iGit interface;
|
||||
|
|
@ -3,43 +3,9 @@ package controller
|
|||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
d "ticket-pimp/domain"
|
||||
"ticket-pimp/ext"
|
||||
d "ticket-pimp/bot/domain"
|
||||
)
|
||||
|
||||
type WorkflowController struct {
|
||||
iGit ext.IGit
|
||||
iCloud ext.ICloud
|
||||
iYouTrack ext.IYouTrack
|
||||
iCoda ext.ICoda
|
||||
}
|
||||
|
||||
func NewWorkflowController(
|
||||
gitBaseURL,
|
||||
gitToken,
|
||||
cloudBaseURL,
|
||||
cloudAuthUser,
|
||||
cloudAuthPass,
|
||||
ytBaseURL,
|
||||
ytToken string,
|
||||
) *WorkflowController {
|
||||
return &WorkflowController{
|
||||
iGit: ext.NewGit(gitBaseURL, gitToken),
|
||||
iCloud: ext.NewCloud(cloudBaseURL, cloudAuthUser, cloudAuthPass),
|
||||
iYouTrack: ext.NewYT(ytBaseURL, ytToken),
|
||||
iCoda: ext.NewCodaClient(),
|
||||
}
|
||||
}
|
||||
|
||||
type IWorkflowController interface {
|
||||
Workflow(name string) (string, error)
|
||||
CreateRepo(name string) (*d.Git, error)
|
||||
CreateFolder(name string) (*d.Folder, error)
|
||||
|
||||
NewTask(summ, desc, c, cLink string) *Task
|
||||
CreateTask(t *Task) (*Task, error)
|
||||
}
|
||||
|
||||
func (wc *WorkflowController) Workflow(name string) (string, error) {
|
||||
yt := wc.iYouTrack
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package domain
|
||||
|
||||
type ConversionLog struct {
|
||||
Advertiser []string
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
d "ticket-pimp/domain"
|
||||
"ticket-pimp/helpers"
|
||||
d "ticket-pimp/bot/domain"
|
||||
"ticket-pimp/bot/helpers"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -3,8 +3,8 @@ package ext
|
|||
import (
|
||||
"log"
|
||||
"os"
|
||||
"ticket-pimp/domain"
|
||||
"ticket-pimp/helpers"
|
||||
"ticket-pimp/bot/domain"
|
||||
"ticket-pimp/bot/helpers"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"log"
|
||||
"time"
|
||||
|
||||
d "ticket-pimp/domain"
|
||||
d "ticket-pimp/bot/domain"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
|
@ -4,9 +4,10 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"ticket-pimp/controller"
|
||||
d "ticket-pimp/domain"
|
||||
"ticket-pimp/bot/controller"
|
||||
d "ticket-pimp/bot/domain"
|
||||
|
||||
"github.com/mr-linch/go-tg"
|
||||
"github.com/mr-linch/go-tg/tgb"
|
||||
|
|
@ -30,6 +31,7 @@ func NewHandler(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass
|
|||
}
|
||||
|
||||
func (h *Handler) PingHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
|
||||
|
||||
return mu.Answer("pong").DoVoid(ctx)
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ func newGit(d *d.Git) *git {
|
|||
}
|
||||
|
||||
// FYI: Telegram doesn't renders this hyperlink, if the url is localhost 🤷♂️
|
||||
func (g *git) prepareAnswer() string {
|
||||
func (g *git) PrepareAnswer() string {
|
||||
return tg.HTML.Text(
|
||||
tg.HTML.Line(
|
||||
"Repo ",
|
||||
|
|
@ -76,7 +78,7 @@ func (h *Handler) NewRepoHandler(ctx context.Context, mu *tgb.MessageUpdate) err
|
|||
return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx)
|
||||
}
|
||||
|
||||
resp := newGit(g).prepareAnswer()
|
||||
resp := newGit(g).PrepareAnswer()
|
||||
|
||||
return mu.Answer(resp).ParseMode(tg.HTML).DoVoid(ctx)
|
||||
}
|
||||
|
|
@ -175,3 +177,51 @@ func (h *Handler) NewTaskHandler(ctx context.Context, mu *tgb.MessageUpdate) err
|
|||
),
|
||||
)).ParseMode(tg.HTML).DoVoid(ctx)
|
||||
}
|
||||
|
||||
func (h *Handler) NewConversion(ctx context.Context, mu *tgb.MessageUpdate) error {
|
||||
msg := strings.TrimSpace(strings.Replace(mu.Caption, "/conversion", "", 1))
|
||||
|
||||
appID, token := normalizeToken(msg)
|
||||
|
||||
fid := mu.Update.Message.Document.FileID
|
||||
|
||||
client := mu.Client
|
||||
|
||||
file, err := client.GetFile(fid).Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := client.Download(ctx, file.FilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
l := h.workflow.ThrowConversions(f, appID, token)
|
||||
|
||||
if len(l.Advertiser) != 0 {
|
||||
return mu.Answer(tg.HTML.Text(
|
||||
"Неуспешные запросы:",
|
||||
tg.HTML.Code(strings.Join(l.Advertiser, ", ")),
|
||||
)).ParseMode(tg.HTML).DoVoid(ctx)
|
||||
}
|
||||
|
||||
return mu.Answer(tg.HTML.Text(
|
||||
"Конверсии отправлены",
|
||||
)).ParseMode(tg.HTML).DoVoid(ctx)
|
||||
}
|
||||
|
||||
func normalizeToken(msg string) (string, string) {
|
||||
msg = strings.TrimSpace(msg)
|
||||
|
||||
args := strings.Split(msg, "|")
|
||||
|
||||
if len(args) != 2 {
|
||||
log.Print(len(args))
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return args[0], args[0] + "|" + args[1]
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"ticket-pimp/bot/domain"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
arg domain.Git
|
||||
expected string
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{domain.Git{
|
||||
Name: "text",
|
||||
FullName: "",
|
||||
Private: false,
|
||||
Url: "",
|
||||
CloneUrl: "",
|
||||
HtmlUrl: "https://reddit.com/",
|
||||
SshUrl: "",
|
||||
}, "Repo <a href=\"https://reddit.com/\">text</a> has been created!"},
|
||||
}
|
||||
|
||||
func TestPrepareAnswer(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
g := newGit(&test.arg)
|
||||
|
||||
if output := g.PrepareAnswer(); output != test.expected {
|
||||
t.Errorf("Output %q not equal to expected %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
cmd/main.go
18
cmd/main.go
|
|
@ -7,7 +7,7 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"ticket-pimp/handler"
|
||||
"ticket-pimp/bot/handler"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/mr-linch/go-tg"
|
||||
|
|
@ -22,20 +22,27 @@ func main() {
|
|||
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
if err := run(ctx); err != nil {
|
||||
if err := runBot(ctx); err != nil {
|
||||
fmt.Println(err)
|
||||
defer os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// env
|
||||
// env function reads provided file and setup envirmental variables;
|
||||
func env(envFilePath string) {
|
||||
err := godotenv.Load(envFilePath)
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
log.Fatal("Error while loading env file")
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context) error {
|
||||
// runBot ...
|
||||
// ..function creates new Telegram BOT instance
|
||||
// ..throw env variables through bot's handlers
|
||||
// ..setup tg bot router;
|
||||
// and finally returns tgb.Poller
|
||||
func runBot(ctx context.Context) error {
|
||||
|
||||
client := tg.New(os.Getenv("TG_API"))
|
||||
|
||||
|
|
@ -54,7 +61,8 @@ func run(ctx context.Context) error {
|
|||
Message(h.PingHandler, tgb.Command("ping")).
|
||||
Message(h.NewRepoHandler, tgb.TextHasPrefix("/repo")).
|
||||
Message(h.NewFolderHandler, tgb.TextHasPrefix("/folder")).
|
||||
Message(h.NewTaskHandler, tgb.TextHasPrefix("/task"))
|
||||
Message(h.NewTaskHandler, tgb.TextHasPrefix("/task")).
|
||||
Message(h.NewConversion, tgb.TextHasPrefix("/conversion"))
|
||||
|
||||
return tgb.NewPoller(
|
||||
router,
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/artsafin/coda-go-client v1.0.4 // indirect
|
||||
github.com/bytedance/sonic v1.9.2 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.13.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
|
|
@ -38,10 +39,13 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/quic-go/quic-go v0.35.1 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 // indirect
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -107,6 +107,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
|
||||
|
|
|
|||
11
readme.md
11
readme.md
|
|
@ -1,5 +1,12 @@
|
|||
# Сборка и запуск:
|
||||
Первые шаги делаю на локальной машине:
|
||||
1. Поменять в коде файл окружения на '.env'
|
||||
2. Собрать контейнер: `docker build -t naudachu/ticket-pimp:latest --pull .`
|
||||
3. Затолкать контейнер в docker hub: `docker push naudachu/ticket-pimp:latest`
|
||||
|
||||
`docker build -t golang-scratch-test --pull .`
|
||||
Далее с сервера:
|
||||
1. Вытягиваем новый образ: `docker pull naudachu/ticket-pimp`
|
||||
2. Запускаем в фоне: `docker run -d naudachu/ticket-pimp`
|
||||
|
||||
# To-do P1:
|
||||
|
||||
|
|
@ -22,4 +29,4 @@
|
|||
- [ ] Складывать в описание репозитория ссылку на тикет;
|
||||
- [ ] Сделать базулю с достойными пользователями;
|
||||
|
||||
- [x] Run bot on docker scratch: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/README.md
|
||||
- [x] Run bot on docker scratch: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/README.mdа
|
||||
Loading…
Reference in New Issue