From 1ee38a298958f13ea76f8ae9686c5eeb14b64e4d Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 15:01:32 +0200 Subject: [PATCH 01/13] initial dalle --- go.mod | 1 + go.sum | 3 ++ internal/telegram/generate.go | 85 +++++++++++++++++++++++++++++++++++ internal/telegram/handler.go | 14 ++++++ internal/telegram/invoice.go | 2 + 5 files changed, 105 insertions(+) create mode 100644 internal/telegram/generate.go diff --git a/go.mod b/go.mod index b7c86b3..75fb068 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/lru v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dillonstreator/dalle v0.0.0-20220826164958-b15c236ceadc // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-redis/redis/v8 v8.8.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9ede352..83ad0a9 100644 --- a/go.sum +++ b/go.sum @@ -219,6 +219,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dillonstreator/dalle v0.0.0-20220826164958-b15c236ceadc h1:gI9DEfWBOylVhQSIAuvKbaTkqvtlDmOTKGcg7i3Y3uE= +github.com/dillonstreator/dalle v0.0.0-20220826164958-b15c236ceadc/go.mod h1:FyvK9n5UiK1wDTgGSkyMckbBAYVZiqaTH1LINKCgbIQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= @@ -1111,6 +1113,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go new file mode 100644 index 0000000..324e1dc --- /dev/null +++ b/internal/telegram/generate.go @@ -0,0 +1,85 @@ +package telegram + +import ( + "bytes" + "context" + "fmt" + "github.com/LightningTipBot/LightningTipBot/internal/runtime" + "github.com/LightningTipBot/LightningTipBot/internal/telegram/intercept" + "github.com/dillonstreator/dalle" + log "github.com/sirupsen/logrus" + "github.com/skip2/go-qrcode" + tb "gopkg.in/lightningtipbot/telebot.v3" + "time" +) + +func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, error) { + user := LoadUser(ctx) + if user.Wallet == nil { + return ctx, fmt.Errorf("user has no wallet") + } + invoice, err := bot.createInvoiceWithEvent(ctx, user, 1, "Pay invoice for dalle", InvoiceCallbackGenerateDalle, "") + if err != nil { + return ctx, err + } + // create qr code + qr, err := qrcode.Encode(invoice.PaymentRequest, qrcode.Medium, 256) + if err != nil { + bot.tryEditMessage(invoice.Message, Translate(ctx, "errorTryLaterMessage")) + return ctx, err + } + + // send the invoice data to user + msg := bot.trySendMessage(ctx.Message().Sender, &tb.Photo{File: tb.File{FileReader: bytes.NewReader(qr)}, Caption: fmt.Sprintf("`%s`", invoice.PaymentRequest)}) + invoice.InvoiceMessage = msg + runtime.IgnoreError(bot.Bunt.Set(invoice)) + bot.trySendMessage(user.Telegram, "dawg") + return ctx, nil +} + +func (bot *TipBot) generateDalleImages(event Event) { + invoiceEvent := event.(*InvoiceEvent) + user := invoiceEvent.User + if user.Wallet == nil { + return + } + // create the client with the bearer token api key + dalleClient, err := dalle.NewHTTPClient("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") + // handle err + if err != nil { + return + } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + defer cancel() + // generate a task to create an image with a prompt + task, err := dalleClient.Generate(ctx, "monkey printing bitcoin with money printing machine, cyberpunk") + // handle err + + // poll the task.ID until status is succeeded + var t *dalle.Task + for { + time.Sleep(time.Second * 3) + + t, err = dalleClient.GetTask(ctx, task.ID) + // handle err + + if t.Status == dalle.StatusSucceeded { + fmt.Println("task succeeded") + break + } else if t.Status == dalle.StatusRejected { + log.Fatal("rejected: ", t.ID) + } + + fmt.Println("task still pending") + } + + // download the first generated image + for _, data := range t.Generations.Data { + reader, err := dalleClient.Download(ctx, data.ID) + if err != nil { + continue + } + bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) + } + // handle err and close readCloser +} diff --git a/internal/telegram/handler.go b/internal/telegram/handler.go index 04347f7..39a4656 100644 --- a/internal/telegram/handler.go +++ b/internal/telegram/handler.go @@ -99,6 +99,20 @@ func (bot TipBot) getHandler() []InterceptionWrapper { }, }, }, + { + Endpoints: []interface{}{"/generate"}, + Handler: bot.generateImages, + Interceptor: &Interceptor{ + Before: []intercept.Func{ + bot.localizerInterceptor, + bot.loadUserInterceptor, + bot.lockInterceptor, + }, + OnDefer: []intercept.Func{ + bot.unlockInterceptor, + }, + }, + }, { Endpoints: []interface{}{"/tip", "/t", "/honk"}, Handler: bot.tipHandler, diff --git a/internal/telegram/invoice.go b/internal/telegram/invoice.go index 9575898..bca30d9 100644 --- a/internal/telegram/invoice.go +++ b/internal/telegram/invoice.go @@ -40,6 +40,7 @@ func initInvoiceEventCallbacks(bot *TipBot) { InvoiceCallbackLNURLPayReceive: EventHandler{Function: bot.lnurlReceiveEvent, Type: EventTypeInvoice}, InvoiceCallbackGroupTicket: EventHandler{Function: bot.groupGetInviteLinkHandler, Type: EventTypeInvoice}, InvoiceCallbackSatdressProxy: EventHandler{Function: bot.satdressProxyRelayPaymentHandler, Type: EventTypeInvoice}, + InvoiceCallbackGenerateDalle: EventHandler{Function: bot.generateDalleImages, Type: EventTypeInvoice}, } } @@ -51,6 +52,7 @@ const ( InvoiceCallbackLNURLPayReceive InvoiceCallbackGroupTicket InvoiceCallbackSatdressProxy + InvoiceCallbackGenerateDalle ) const ( From 98bc70fc7c0548b3e2af873f3f8b67f36965ddd6 Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 15:20:41 +0200 Subject: [PATCH 02/13] update invoice + mock --- internal/telegram/generate.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 324e1dc..9fd3336 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -10,6 +10,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/skip2/go-qrcode" tb "gopkg.in/lightningtipbot/telebot.v3" + "os" "time" ) @@ -18,7 +19,12 @@ func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, err if user.Wallet == nil { return ctx, fmt.Errorf("user has no wallet") } - invoice, err := bot.createInvoiceWithEvent(ctx, user, 1, "Pay invoice for dalle", InvoiceCallbackGenerateDalle, "") + me, err := GetUser(bot.Telegram.Me, *bot) + if err != nil { + return ctx, err + } + invoice, err := bot.createInvoiceWithEvent(ctx, me, 1, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, "") + if err != nil { return ctx, err } @@ -33,7 +39,6 @@ func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, err msg := bot.trySendMessage(ctx.Message().Sender, &tb.Photo{File: tb.File{FileReader: bytes.NewReader(qr)}, Caption: fmt.Sprintf("`%s`", invoice.PaymentRequest)}) invoice.InvoiceMessage = msg runtime.IgnoreError(bot.Bunt.Set(invoice)) - bot.trySendMessage(user.Telegram, "dawg") return ctx, nil } @@ -44,7 +49,7 @@ func (bot *TipBot) generateDalleImages(event Event) { return } // create the client with the bearer token api key - dalleClient, err := dalle.NewHTTPClient("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") + dalleClient, err := dalle.NewHTTPClient("") // handle err if err != nil { return @@ -62,6 +67,7 @@ func (bot *TipBot) generateDalleImages(event Event) { t, err = dalleClient.GetTask(ctx, task.ID) // handle err + t.Status = dalle.StatusSucceeded if t.Status == dalle.StatusSucceeded { fmt.Println("task succeeded") @@ -72,14 +78,19 @@ func (bot *TipBot) generateDalleImages(event Event) { fmt.Println("task still pending") } - - // download the first generated image - for _, data := range t.Generations.Data { - reader, err := dalleClient.Download(ctx, data.ID) - if err != nil { - continue - } - bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) + /* + // download the first generated image + for _, data := range t.Generations.Data { + reader, err := dalleClient.Download(ctx, data.ID) + if err != nil { + continue + } + bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) + }*/ + reader, err := os.OpenFile("image", 0, os.ModePerm) + if err != nil { + panic(err) } + bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) // handle err and close readCloser } From 92f9ba1550dfa3d3baaab71e418ff18efa3052e5 Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 16:15:14 +0200 Subject: [PATCH 03/13] added dalle --- internal/dalle/client.go | 14 ++ internal/dalle/dalle.go | 11 ++ internal/dalle/httpclient.go | 259 ++++++++++++++++++++++++++++++++++ internal/telegram/generate.go | 50 ++++--- 4 files changed, 316 insertions(+), 18 deletions(-) create mode 100644 internal/dalle/client.go create mode 100644 internal/dalle/dalle.go create mode 100644 internal/dalle/httpclient.go diff --git a/internal/dalle/client.go b/internal/dalle/client.go new file mode 100644 index 0000000..42a7ca5 --- /dev/null +++ b/internal/dalle/client.go @@ -0,0 +1,14 @@ +package dalle + +import ( + "context" + "io" +) + +type Client interface { + Generate(ctx context.Context, prompt string) (*Task, error) + ListTasks(ctx context.Context, req *ListTasksRequest) (*ListTasksResponse, error) + GetTask(ctx context.Context, taskID string) (*Task, error) + Download(ctx context.Context, generationID string) (io.ReadCloser, error) + Share(ctx context.Context, generationID string) (string, error) +} diff --git a/internal/dalle/dalle.go b/internal/dalle/dalle.go new file mode 100644 index 0000000..7a96853 --- /dev/null +++ b/internal/dalle/dalle.go @@ -0,0 +1,11 @@ +package dalle + +const ( + StatusPending = "pending" + StatusRejected = "rejected" + StatusSucceeded = "succeeded" + + TaskTypeText2Im = "text2im" + + defaultBatchSize = 4 +) diff --git a/internal/dalle/httpclient.go b/internal/dalle/httpclient.go new file mode 100644 index 0000000..c0c4aff --- /dev/null +++ b/internal/dalle/httpclient.go @@ -0,0 +1,259 @@ +package dalle + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +const ( + libraryVersion = "1.0.0" + defaultUserAgent = "dalle/" + libraryVersion + baseURL = "https://labs.openai.com/api/labs" + + defaultHTTPClientTimeout = 15 * time.Second +) + +type option func(*HTTPClient) error + +func WithHTTPClient(httpClient *http.Client) option { + return func(c *HTTPClient) error { + c.httpClient = httpClient + + return nil + } +} + +func WithUserAgent(userAgent string) option { + return func(c *HTTPClient) error { + c.userAgent = userAgent + + return nil + } +} + +type HTTPClient struct { + httpClient *http.Client + userAgent string + apiKey string +} + +var _ Client = (*HTTPClient)(nil) + +func NewHTTPClient(apiKey string, opts ...option) (*HTTPClient, error) { + c := &HTTPClient{ + httpClient: &http.Client{Timeout: defaultHTTPClientTimeout}, + userAgent: defaultUserAgent, + apiKey: apiKey, + } + + for _, opt := range opts { + if err := opt(c); err != nil { + return nil, err + } + } + + return c, nil +} + +type Task struct { + Object string `json:"object"` + ID string `json:"id"` + Created int64 `json:"created"` + TaskType string `json:"task_type"` + Status string `json:"status"` + PromptID string `json:"prompt_id"` + Prompt Prompt `json:"prompt"` + Generations Generations `json:"generations"` +} +type Generations struct { + Data []GenerationData `json:"data"` + Object string `json:"object"` +} +type GenerationData struct { + Created int64 `json:"created"` + Generation Generation `json:"generation"` + GenerationType string `json:"generation_type"` + ID string `json:"id"` +} +type Generation struct { + ImagePath string `json:"image_path"` +} + +type Prompt struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + PromptType string `json:"prompt_type"` + Prompt struct { + Caption string `json:"caption"` + } `json:"prompt"` + ParentGenerationID string `json:"parent_generation_id"` +} + +type GenerateRequest struct { + Prompt GenerateRequestPrompt `json:"prompt"` + TaskType string `json:"task_type"` +} +type GenerateRequestPrompt struct { + BatchSize int32 `json:"batch_size"` + Caption string `json:"caption"` +} + +func (c *HTTPClient) Generate(ctx context.Context, caption string) (*Task, error) { + task := &Task{} + req := &GenerateRequest{ + Prompt: GenerateRequestPrompt{ + BatchSize: defaultBatchSize, + Caption: caption, + }, + TaskType: TaskTypeText2Im, + } + return task, c.request(ctx, "POST", "/tasks", nil, req, task) +} + +type ListTasksResponse struct { + Object string `json:"object"` + Data []Task `json:"data"` +} + +type ListTasksRequest struct { + Limit int32 `json:"limit"` +} + +func (c *HTTPClient) ListTasks(ctx context.Context, req *ListTasksRequest) (*ListTasksResponse, error) { + res := &ListTasksResponse{} + url := "/tasks" + if req != nil { + if req.Limit != 0 { + url += fmt.Sprintf("?limit=%d", req.Limit) + } + } + + return res, c.request(ctx, "GET", url, nil, nil, res) +} + +func (c *HTTPClient) GetTask(ctx context.Context, taskID string) (*Task, error) { + task := &Task{} + return task, c.request(ctx, "GET", "/tasks/"+taskID, nil, nil, task) +} + +func (c *HTTPClient) Download(ctx context.Context, generationID string) (io.ReadCloser, error) { + req, err := c.createRequest(ctx, "/generations/"+generationID+"/download", "GET", nil, nil) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("performing request: %w", err) + } + + return resp.Body, nil +} + +// Share makes the generation public and returns the public url +func (c *HTTPClient) Share(ctx context.Context, generationID string) (string, error) { + res := &GenerationData{} + + err := c.request(ctx, "POST", "/generations/"+generationID+"/share", nil, nil, res) + if err != nil { + return "", err + } + + return res.Generation.ImagePath, nil +} + +func (c *HTTPClient) createRequest(ctx context.Context, path, method string, values *url.Values, data interface{}) (*http.Request, error) { + url := baseURL + path + + if values != nil { + url += "?" + values.Encode() + } + + var body io.Reader + if data != nil { + b, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("parsing request data: %w", err) + } + body = bytes.NewReader(b) + } + + req, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + return nil, fmt.Errorf("building request: %w", err) + } + + req.Header.Add("Authorization", "Bearer "+c.apiKey) + req.Header.Add("Content-Type", "application/json") + req.Header.Set("User-Agent", c.userAgent) + + req.Header.Set("Authority", "labs.openai.com") + req.Header.Set("Accept", "*/*") + req.Header.Set("Accept-Language", "en-US,en;q=0.9,de;q=0.8") + req.Header.Set("Cache-Control", "no-cache") + req.Header.Set("Content-Length", "0") + req.Header.Set("Cookie", "") + req.Header.Set("Dnt", "1") + req.Header.Set("Origin", "https://labs.openai.com") + req.Header.Set("Pragma", "no-cache") + req.Header.Set("Sec-Ch-Ua", "\"Chromium\";v=\"104\", \" Not A;Brand\";v=\"99\", \"Google Chrome\";v=\"104\"") + req.Header.Set("Sec-Ch-Ua-Mobile", "?0") + req.Header.Set("Sec-Ch-Ua-Platform", "\"macOS\"") + req.Header.Set("Sec-Fetch-Dest", "empty") + req.Header.Set("Sec-Fetch-Mode", "cors") + req.Header.Set("Sec-Fetch-Site", "same-origin") + req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36") + + return req, nil +} + +func (c *HTTPClient) request(ctx context.Context, method, path string, values *url.Values, body interface{}, result interface{}) error { + req, err := c.createRequest(ctx, path, method, values, body) + if err != nil { + return fmt.Errorf("creating request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("performing request: %w", err) + } + defer resp.Body.Close() + + respBody, _ := io.ReadAll(resp.Body) + + // TODO: improve error handling... + if resp.StatusCode != http.StatusOK { + return Error{ + Message: "unexpected non 200 status code", + StatusCode: resp.StatusCode, + Details: string(respBody), + } + } + + if err = json.Unmarshal(respBody, result); err != nil { + return Error{ + Message: err.Error(), + StatusCode: resp.StatusCode, + Details: string(respBody), + } + } + + return nil +} + +type Error struct { + Message string + StatusCode int + Details string +} + +func (e Error) Error() string { + return fmt.Sprintf("dalle: %s (status: %d, details: %s)", e.Message, e.StatusCode, e.Details) +} diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 9fd3336..f292755 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" + "github.com/LightningTipBot/LightningTipBot/internal/dalle" "github.com/LightningTipBot/LightningTipBot/internal/runtime" "github.com/LightningTipBot/LightningTipBot/internal/telegram/intercept" - "github.com/dillonstreator/dalle" log "github.com/sirupsen/logrus" "github.com/skip2/go-qrcode" tb "gopkg.in/lightningtipbot/telebot.v3" + "io" "os" "time" ) @@ -24,7 +25,7 @@ func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, err return ctx, err } invoice, err := bot.createInvoiceWithEvent(ctx, me, 1, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, "") - + invoice.Payer = user if err != nil { return ctx, err } @@ -44,20 +45,22 @@ func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, err func (bot *TipBot) generateDalleImages(event Event) { invoiceEvent := event.(*InvoiceEvent) - user := invoiceEvent.User + user := invoiceEvent.Payer if user.Wallet == nil { return } // create the client with the bearer token api key + dalleClient, err := dalle.NewHTTPClient("") // handle err if err != nil { return } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) defer cancel() // generate a task to create an image with a prompt - task, err := dalleClient.Generate(ctx, "monkey printing bitcoin with money printing machine, cyberpunk") + task, err := dalleClient.Generate(ctx, "dogs fighting for bitcoin on sunny island, van gogh style") // handle err // poll the task.ID until status is succeeded @@ -67,7 +70,6 @@ func (bot *TipBot) generateDalleImages(event Event) { t, err = dalleClient.GetTask(ctx, task.ID) // handle err - t.Status = dalle.StatusSucceeded if t.Status == dalle.StatusSucceeded { fmt.Println("task succeeded") @@ -78,19 +80,31 @@ func (bot *TipBot) generateDalleImages(event Event) { fmt.Println("task still pending") } - /* - // download the first generated image - for _, data := range t.Generations.Data { - reader, err := dalleClient.Download(ctx, data.ID) - if err != nil { - continue - } - bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) - }*/ - reader, err := os.OpenFile("image", 0, os.ModePerm) - if err != nil { - panic(err) + + // download the first generated image + for _, data := range t.Generations.Data { + + reader, err := dalleClient.Download(ctx, data.ID) + if err != nil { + return + } + defer reader.Close() + + file, err := os.Create("images/" + data.ID + ".png") + if err != nil { + return + } + defer file.Close() + _, err = io.Copy(file, reader) + if err != nil { + return + } + f, err := os.OpenFile("images/"+data.ID+".png", 0, os.ModePerm) + if err != nil { + return + } + bot.trySendMessage(invoiceEvent.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}, Caption: fmt.Sprintf("Result")}) } - bot.trySendMessage(user.Telegram, &tb.Photo{File: tb.File{FileReader: reader}, Caption: fmt.Sprintf("Result")}) + // handle err and close readCloser } From 069029fbb3d8ed395f15fc0bd48209b88154d173 Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 16:25:59 +0200 Subject: [PATCH 04/13] fixes --- internal/dalle/httpclient.go | 17 ----------------- internal/telegram/generate.go | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/internal/dalle/httpclient.go b/internal/dalle/httpclient.go index c0c4aff..462bf68 100644 --- a/internal/dalle/httpclient.go +++ b/internal/dalle/httpclient.go @@ -194,23 +194,6 @@ func (c *HTTPClient) createRequest(ctx context.Context, path, method string, val req.Header.Add("Content-Type", "application/json") req.Header.Set("User-Agent", c.userAgent) - req.Header.Set("Authority", "labs.openai.com") - req.Header.Set("Accept", "*/*") - req.Header.Set("Accept-Language", "en-US,en;q=0.9,de;q=0.8") - req.Header.Set("Cache-Control", "no-cache") - req.Header.Set("Content-Length", "0") - req.Header.Set("Cookie", "") - req.Header.Set("Dnt", "1") - req.Header.Set("Origin", "https://labs.openai.com") - req.Header.Set("Pragma", "no-cache") - req.Header.Set("Sec-Ch-Ua", "\"Chromium\";v=\"104\", \" Not A;Brand\";v=\"99\", \"Google Chrome\";v=\"104\"") - req.Header.Set("Sec-Ch-Ua-Mobile", "?0") - req.Header.Set("Sec-Ch-Ua-Platform", "\"macOS\"") - req.Header.Set("Sec-Fetch-Dest", "empty") - req.Header.Set("Sec-Fetch-Mode", "cors") - req.Header.Set("Sec-Fetch-Site", "same-origin") - req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36") - return req, nil } diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index f292755..50cb27f 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -60,7 +60,7 @@ func (bot *TipBot) generateDalleImages(event Event) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) defer cancel() // generate a task to create an image with a prompt - task, err := dalleClient.Generate(ctx, "dogs fighting for bitcoin on sunny island, van gogh style") + task, err := dalleClient.Generate(ctx, "humanoid robot standing on a skyscraper at night looking down on the dark rainy metropolis in vaporware style oil painting") // handle err // poll the task.ID until status is succeeded From 890448ce81f4db8e2eeed8205221ea000019bce7 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 17:16:38 +0200 Subject: [PATCH 05/13] ux --- internal/lnbits/types.go | 1 + internal/telegram/generate.go | 71 ++++++++++++++++++++++++++++++----- internal/telegram/state.go | 1 + translations/en.toml | 8 +++- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/internal/lnbits/types.go b/internal/lnbits/types.go index 770359a..52fa7a9 100644 --- a/internal/lnbits/types.go +++ b/internal/lnbits/types.go @@ -65,6 +65,7 @@ const ( UserStateShopItemSendPrice UserStateShopItemSendItemFile UserEnterShopsDescription + UserEnterDallePrompt ) type UserStateKey int diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 50cb27f..81cef2e 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -4,31 +4,81 @@ import ( "bytes" "context" "fmt" + "io" + "os" + "strings" + "time" + "github.com/LightningTipBot/LightningTipBot/internal/dalle" + "github.com/LightningTipBot/LightningTipBot/internal/lnbits" "github.com/LightningTipBot/LightningTipBot/internal/runtime" "github.com/LightningTipBot/LightningTipBot/internal/telegram/intercept" log "github.com/sirupsen/logrus" "github.com/skip2/go-qrcode" tb "gopkg.in/lightningtipbot/telebot.v3" - "io" - "os" - "time" ) +const DALLE2PRICE = 100 // satoshis + +// generateImages is called when the user enters /generate or /generate +// asks the user for a prompt if not given func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, error) { user := LoadUser(ctx) if user.Wallet == nil { return ctx, fmt.Errorf("user has no wallet") } + + if len(strings.Split(ctx.Message().Text, " ")) < 2 { + // We need to save the pay state in the user state so we can load the payment in the next handler + SetUserState(user, bot, lnbits.UserEnterDallePrompt, "") + bot.trySendMessage(ctx.Message().Sender, "⌨️ Enter image prompt.", tb.ForceReply) + return ctx, nil + } + // write the prompt into the command and call confirm + m := ctx.Message() + m.Text = GetMemoFromCommand(m.Text, 1) + return bot.confirmGenerateImages(ctx) +} + +// confirmGenerateImages is called when the user has entered a prompt through /generate +// or because he answered to the request to enter it in generateImages() +// confirmGenerateImages will create an invoice that the user can pay and if they pay +// generateDalleImages will fetch the images and send it to the user +func (bot *TipBot) confirmGenerateImages(ctx intercept.Context) (intercept.Context, error) { + user := LoadUser(ctx) + + ResetUserState(user, bot) + m := ctx.Message() + prompt := m.Text + if user.Wallet == nil { + return ctx, fmt.Errorf("user has no wallet") + } me, err := GetUser(bot.Telegram.Me, *bot) if err != nil { return ctx, err } - invoice, err := bot.createInvoiceWithEvent(ctx, me, 1, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, "") + invoice, err := bot.createInvoiceWithEvent(ctx, me, DALLE2PRICE, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, prompt) invoice.Payer = user if err != nil { return ctx, err } + + runtime.IgnoreError(bot.Bunt.Set(invoice)) + + balance, err := bot.GetUserBalance(user) + if err != nil { + errmsg := fmt.Sprintf("[inlineReceive] Error: Could not get user balance: %s", err.Error()) + log.Warnln(errmsg) + } + + bot.trySendMessage(ctx.Message().Sender, Translate(ctx, "generateDallePayInvoiceMessage")) + + // invoke internal pay if enough balance + if balance > DALLE2PRICE { + m.Text = fmt.Sprintf("/pay %s", invoice.PaymentRequest) + return bot.payHandler(ctx) + } + // create qr code qr, err := qrcode.Encode(invoice.PaymentRequest, qrcode.Medium, 256) if err != nil { @@ -43,15 +93,16 @@ func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, err return ctx, nil } +// generateDalleImages is called by the invoice event when the user has paid func (bot *TipBot) generateDalleImages(event Event) { invoiceEvent := event.(*InvoiceEvent) user := invoiceEvent.Payer - if user.Wallet == nil { + if user == nil || user.Wallet == nil { return } // create the client with the bearer token api key - dalleClient, err := dalle.NewHTTPClient("") + dalleClient, err := dalle.NewHTTPClient("API KEY") // handle err if err != nil { return @@ -60,8 +111,10 @@ func (bot *TipBot) generateDalleImages(event Event) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) defer cancel() // generate a task to create an image with a prompt - task, err := dalleClient.Generate(ctx, "humanoid robot standing on a skyscraper at night looking down on the dark rainy metropolis in vaporware style oil painting") - // handle err + task, err := dalleClient.Generate(ctx, invoiceEvent.CallbackData) + if err != nil { + + } // poll the task.ID until status is succeeded var t *dalle.Task @@ -103,7 +156,7 @@ func (bot *TipBot) generateDalleImages(event Event) { if err != nil { return } - bot.trySendMessage(invoiceEvent.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}, Caption: fmt.Sprintf("Result")}) + bot.trySendMessage(invoiceEvent.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}}) } // handle err and close readCloser diff --git a/internal/telegram/state.go b/internal/telegram/state.go index f3135c2..3839d94 100644 --- a/internal/telegram/state.go +++ b/internal/telegram/state.go @@ -20,5 +20,6 @@ func initializeStateCallbackMessage(bot *TipBot) { lnbits.UserStateShopItemSendTitle: bot.enterShopItemTitleHandler, lnbits.UserStateShopItemSendItemFile: bot.addItemFileHandler, lnbits.UserEnterShopsDescription: bot.enterShopsDescriptionHandler, + lnbits.UserEnterDallePrompt: bot.confirmGenerateImages, } } diff --git a/translations/en.toml b/translations/en.toml index 19cd7c3..6d3d5bd 100644 --- a/translations/en.toml +++ b/translations/en.toml @@ -133,6 +133,7 @@ advancedMessage = """%s enterAmountRangeMessage = """πŸ’― Enter an amount between %d and %d sat.""" enterAmountMessage = """πŸ’― Enter an amount.""" enterUserMessage = """πŸ‘€ Enter a user.""" +enterTextMessage = """⌨️ Enter text.""" errorReasonMessage = """🚫 Error: %s""" # START @@ -393,4 +394,9 @@ To join a group, talk to %s and write in a private message `/join `. πŸ“– *Usage:* *For admins (in group chat):* `/group add []`\nExample: `/group add TheBestBitcoinGroup 1000` -*For users (in private chat):* `/join `\nExample: `/join TheBestBitcoinGroup`""" \ No newline at end of file +*For users (in private chat):* `/join `\nExample: `/join TheBestBitcoinGroup`""" + +# DALLE GENERATE +generateDalleHelpMessage = """Generate images using OpenAI DALLE 2.\nUsage: `/generate `\nPrice: 1000 sat""" +generateDallePayInvoiceMessage = """Pay this invoice to generate four images πŸ‘‡""" +generateDalleGeneratingMessage = """Your images are being generated...""" \ No newline at end of file From 2e4a793c20dddf824479bf74a16302bcbc4cc33d Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 17:32:45 +0200 Subject: [PATCH 06/13] dalle config --- config.yaml.example | 3 +++ internal/config.go | 6 ++++++ internal/telegram/generate.go | 9 ++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/config.yaml.example b/config.yaml.example index 31166e7..6713a10 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -25,3 +25,6 @@ database: transactions_path: "data/transactions.db" shop_buntdb_path: "data/shop.db" groupsdb_path: "data/groups.db" +generate: + dalle_key: "asd" + dalle_price: 1000 \ No newline at end of file diff --git a/internal/config.go b/internal/config.go index 2f7e1e6..c6c1034 100644 --- a/internal/config.go +++ b/internal/config.go @@ -14,8 +14,14 @@ var Configuration = struct { Telegram TelegramConfiguration `yaml:"telegram"` Database DatabaseConfiguration `yaml:"database"` Lnbits LnbitsConfiguration `yaml:"lnbits"` + Generate GenerateConfiguration `yaml:"generate"` }{} +type GenerateConfiguration struct { + DalleKey string `yaml:"dalle_key"` + DallePrice int64 `yaml:"dalle_price"` +} + type SocksConfiguration struct { Host string `yaml:"host"` Username string `yaml:"username"` diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 81cef2e..cd8f806 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/LightningTipBot/LightningTipBot/internal" "github.com/LightningTipBot/LightningTipBot/internal/dalle" "github.com/LightningTipBot/LightningTipBot/internal/lnbits" "github.com/LightningTipBot/LightningTipBot/internal/runtime" @@ -18,8 +19,6 @@ import ( tb "gopkg.in/lightningtipbot/telebot.v3" ) -const DALLE2PRICE = 100 // satoshis - // generateImages is called when the user enters /generate or /generate // asks the user for a prompt if not given func (bot *TipBot) generateImages(ctx intercept.Context) (intercept.Context, error) { @@ -57,7 +56,7 @@ func (bot *TipBot) confirmGenerateImages(ctx intercept.Context) (intercept.Conte if err != nil { return ctx, err } - invoice, err := bot.createInvoiceWithEvent(ctx, me, DALLE2PRICE, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, prompt) + invoice, err := bot.createInvoiceWithEvent(ctx, me, internal.Configuration.Generate.DallePrice, fmt.Sprintf("DALLE2 %s", GetUserStr(user.Telegram)), InvoiceCallbackGenerateDalle, prompt) invoice.Payer = user if err != nil { return ctx, err @@ -74,7 +73,7 @@ func (bot *TipBot) confirmGenerateImages(ctx intercept.Context) (intercept.Conte bot.trySendMessage(ctx.Message().Sender, Translate(ctx, "generateDallePayInvoiceMessage")) // invoke internal pay if enough balance - if balance > DALLE2PRICE { + if balance >= internal.Configuration.Generate.DallePrice { m.Text = fmt.Sprintf("/pay %s", invoice.PaymentRequest) return bot.payHandler(ctx) } @@ -102,7 +101,7 @@ func (bot *TipBot) generateDalleImages(event Event) { } // create the client with the bearer token api key - dalleClient, err := dalle.NewHTTPClient("API KEY") + dalleClient, err := dalle.NewHTTPClient(internal.Configuration.Generate.DalleKey) // handle err if err != nil { return From 2c4d499cd98e611f7a565443e1ad8cd47fa29228 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 17:40:13 +0200 Subject: [PATCH 07/13] add dalle data folder --- data/dalle/.placeholder | 1 + internal/telegram/generate.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 data/dalle/.placeholder diff --git a/data/dalle/.placeholder b/data/dalle/.placeholder new file mode 100644 index 0000000..284cc65 --- /dev/null +++ b/data/dalle/.placeholder @@ -0,0 +1 @@ +this is where dalle images are stored diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index cd8f806..b7b8b6f 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -142,7 +142,7 @@ func (bot *TipBot) generateDalleImages(event Event) { } defer reader.Close() - file, err := os.Create("images/" + data.ID + ".png") + file, err := os.Create("data/dalle/" + data.ID + ".png") if err != nil { return } @@ -151,7 +151,7 @@ func (bot *TipBot) generateDalleImages(event Event) { if err != nil { return } - f, err := os.OpenFile("images/"+data.ID+".png", 0, os.ModePerm) + f, err := os.OpenFile("data/dalle/"+data.ID+".png", 0, os.ModePerm) if err != nil { return } From 3c862d59ffdf9b8faea616a2a9872a5365c15430 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 17:43:30 +0200 Subject: [PATCH 08/13] docu --- translations/de.toml | 3 ++- translations/en.toml | 4 +++- translations/es.toml | 3 ++- translations/fr.toml | 3 ++- translations/id.toml | 3 ++- translations/it.toml | 3 ++- translations/nl.toml | 3 ++- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/translations/de.toml b/translations/de.toml index 860a310..9d27036 100644 --- a/translations/de.toml +++ b/translations/de.toml @@ -124,7 +124,8 @@ advancedMessage = """%s */faucet* 🚰 Erzeuge einen Zapfhahn: `/faucet ` */tipjar* 🍯 Erzeuge eine Spendendose: `/tipjar ` */group* 🎟 Tickets fΓΌr Gruppenchats: `/group add []` -*/shop* πŸ› Durchsuche shops: `/shop` oder `/shop `""" +*/shop* πŸ› Durchsuche shops: `/shop` oder `/shop ` +*/generate* πŸŽ† Generiere bilder mit DALLE-2: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Gebe Betrag zwuschen %d und %d sat ein.""" diff --git a/translations/en.toml b/translations/en.toml index 6d3d5bd..aba79d0 100644 --- a/translations/en.toml +++ b/translations/en.toml @@ -127,7 +127,9 @@ advancedMessage = """%s */faucet* 🚰 Create a faucet: `/faucet ` */tipjar* 🍯 Create a tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" + # GENERIC enterAmountRangeMessage = """πŸ’― Enter an amount between %d and %d sat.""" diff --git a/translations/es.toml b/translations/es.toml index f642b44..930e183 100644 --- a/translations/es.toml +++ b/translations/es.toml @@ -123,7 +123,8 @@ advancedMessage = """%s */faucet* 🚰 Crear un grifo: `/faucet ` */tipjar* 🍯 Crear un tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Introduce un monto entre %d y %d sat.""" diff --git a/translations/fr.toml b/translations/fr.toml index 2c0c2f1..5ee7454 100644 --- a/translations/fr.toml +++ b/translations/fr.toml @@ -123,7 +123,8 @@ advancedMessage = """%s */faucet* 🚰 CrΓ©er un faucet: `/faucet ` */tipjar* 🍯 CrΓ©er un tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Choisissez un montant entre %d et %d sat.""" diff --git a/translations/id.toml b/translations/id.toml index 52eecf9..07be137 100644 --- a/translations/id.toml +++ b/translations/id.toml @@ -123,7 +123,8 @@ advancedMessage = """%s */faucet* 🚰 Membuat sebuah keran `/faucet ` */tipjar* 🍯 Create a tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Masukkan jumlah diantara %d dan %d sat.""" diff --git a/translations/it.toml b/translations/it.toml index 375046a..839ff5f 100644 --- a/translations/it.toml +++ b/translations/it.toml @@ -123,7 +123,8 @@ advancedMessage = """%s */faucet* 🚰 Crea una distribuzione: `/faucet ` */tipjar* 🍯 Crea un tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Imposta un ammontare tra %d e %d sat.""" diff --git a/translations/nl.toml b/translations/nl.toml index 789349c..6e883c1 100644 --- a/translations/nl.toml +++ b/translations/nl.toml @@ -123,7 +123,8 @@ advancedMessage = """%s */faucet* 🚰 Maak een kraan: `/faucet ` */tipjar* 🍯 Maak een tipjar: `/tipjar ` */group* 🎟 Create group tickets: `/group add []` -*/shop* πŸ› Browse shops: `/shop` or `/shop `""" +*/shop* πŸ› Browse shops: `/shop` or `/shop ` +*/generate* πŸŽ† Generate DALLE-2 images: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Voer een bedrag in tussen %d en %d sat.""" From 6bdd24e4a3fbb34b6a0bab74d36a2cc22873b6bb Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 17:48:56 +0200 Subject: [PATCH 09/13] downloadAndSendImages + log --- internal/telegram/generate.go | 54 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index b7b8b6f..6dd6502 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -97,6 +97,7 @@ func (bot *TipBot) generateDalleImages(event Event) { invoiceEvent := event.(*InvoiceEvent) user := invoiceEvent.Payer if user == nil || user.Wallet == nil { + log.Errorf("[generateDalleImages] invalid user") return } // create the client with the bearer token api key @@ -104,6 +105,7 @@ func (bot *TipBot) generateDalleImages(event Event) { dalleClient, err := dalle.NewHTTPClient(internal.Configuration.Generate.DalleKey) // handle err if err != nil { + log.Errorf("[generateDalleImages] %v", err.Error()) return } @@ -112,7 +114,8 @@ func (bot *TipBot) generateDalleImages(event Event) { // generate a task to create an image with a prompt task, err := dalleClient.Generate(ctx, invoiceEvent.CallbackData) if err != nil { - + log.Errorf("[generateDalleImages] %v", err.Error()) + return } // poll the task.ID until status is succeeded @@ -127,7 +130,7 @@ func (bot *TipBot) generateDalleImages(event Event) { fmt.Println("task succeeded") break } else if t.Status == dalle.StatusRejected { - log.Fatal("rejected: ", t.ID) + log.Errorf("rejected: %s", t.ID) } fmt.Println("task still pending") @@ -135,28 +138,33 @@ func (bot *TipBot) generateDalleImages(event Event) { // download the first generated image for _, data := range t.Generations.Data { - - reader, err := dalleClient.Download(ctx, data.ID) - if err != nil { - return - } - defer reader.Close() - - file, err := os.Create("data/dalle/" + data.ID + ".png") - if err != nil { - return - } - defer file.Close() - _, err = io.Copy(file, reader) - if err != nil { - return - } - f, err := os.OpenFile("data/dalle/"+data.ID+".png", 0, os.ModePerm) - if err != nil { - return - } - bot.trySendMessage(invoiceEvent.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}}) + downloadAndSendImages(ctx, bot, dalleClient, data, invoiceEvent) } // handle err and close readCloser } + +// downloadAndSendImages will download dalle images and send them to the payer. +func downloadAndSendImages(ctx context.Context, bot *TipBot, dalleClient dalle.Client, data dalle.GenerationData, event *InvoiceEvent) { + reader, err := dalleClient.Download(ctx, data.ID) + if err != nil { + return + } + defer reader.Close() + image := "data/dalle/" + data.ID + ".png" + file, err := os.Create(image) + if err != nil { + return + } + defer file.Close() + _, err = io.Copy(file, reader) + if err != nil { + return + } + f, err := os.OpenFile(image, 0, os.ModePerm) + if err != nil { + return + } + defer f.Close() + bot.trySendMessage(event.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}}) +} From 2569f0d12e58d8a912823aa342810e9a3ef6417d Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 17:50:48 +0200 Subject: [PATCH 10/13] error handling --- internal/telegram/generate.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 6dd6502..86bac9d 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -138,33 +138,37 @@ func (bot *TipBot) generateDalleImages(event Event) { // download the first generated image for _, data := range t.Generations.Data { - downloadAndSendImages(ctx, bot, dalleClient, data, invoiceEvent) + err = downloadAndSendImages(ctx, bot, dalleClient, data, invoiceEvent) + if err != nil { + log.Errorf("[downloadAndSendImages] %v", err.Error()) + } } // handle err and close readCloser } // downloadAndSendImages will download dalle images and send them to the payer. -func downloadAndSendImages(ctx context.Context, bot *TipBot, dalleClient dalle.Client, data dalle.GenerationData, event *InvoiceEvent) { +func downloadAndSendImages(ctx context.Context, bot *TipBot, dalleClient dalle.Client, data dalle.GenerationData, event *InvoiceEvent) error { reader, err := dalleClient.Download(ctx, data.ID) if err != nil { - return + return err } defer reader.Close() image := "data/dalle/" + data.ID + ".png" file, err := os.Create(image) if err != nil { - return + return err } defer file.Close() _, err = io.Copy(file, reader) if err != nil { - return + return err } f, err := os.OpenFile(image, 0, os.ModePerm) if err != nil { - return + return err } defer f.Close() bot.trySendMessage(event.Payer.Telegram, &tb.Photo{File: tb.File{FileReader: f}}) + return nil } From d992d5858f1cd8373b16904a69ae3f5319e82e82 Mon Sep 17 00:00:00 2001 From: gohumble Date: Tue, 30 Aug 2022 17:51:43 +0200 Subject: [PATCH 11/13] error handling --- internal/telegram/generate.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 86bac9d..5fc938a 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -105,7 +105,7 @@ func (bot *TipBot) generateDalleImages(event Event) { dalleClient, err := dalle.NewHTTPClient(internal.Configuration.Generate.DalleKey) // handle err if err != nil { - log.Errorf("[generateDalleImages] %v", err.Error()) + log.Errorf("[NewHTTPClient] %v", err.Error()) return } @@ -114,7 +114,7 @@ func (bot *TipBot) generateDalleImages(event Event) { // generate a task to create an image with a prompt task, err := dalleClient.Generate(ctx, invoiceEvent.CallbackData) if err != nil { - log.Errorf("[generateDalleImages] %v", err.Error()) + log.Errorf("[Generate] %v", err.Error()) return } @@ -125,7 +125,10 @@ func (bot *TipBot) generateDalleImages(event Event) { t, err = dalleClient.GetTask(ctx, task.ID) // handle err - + if err != nil { + log.Errorf("[GetTask] %v", err.Error()) + return + } if t.Status == dalle.StatusSucceeded { fmt.Println("task succeeded") break From fea7281e29137d2e70f18d10fcb2be9c139d2b5f Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 18:05:01 +0200 Subject: [PATCH 12/13] lesgo --- internal/telegram/generate.go | 5 +++-- translations/en.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/telegram/generate.go b/internal/telegram/generate.go index 5fc938a..5f02e11 100644 --- a/internal/telegram/generate.go +++ b/internal/telegram/generate.go @@ -100,8 +100,10 @@ func (bot *TipBot) generateDalleImages(event Event) { log.Errorf("[generateDalleImages] invalid user") return } - // create the client with the bearer token api key + bot.trySendMessage(user.Telegram, "Your images are being generated. Please wait...") + + // create the client with the bearer token api key dalleClient, err := dalle.NewHTTPClient(internal.Configuration.Generate.DalleKey) // handle err if err != nil { @@ -117,7 +119,6 @@ func (bot *TipBot) generateDalleImages(event Event) { log.Errorf("[Generate] %v", err.Error()) return } - // poll the task.ID until status is succeeded var t *dalle.Task for { diff --git a/translations/en.toml b/translations/en.toml index aba79d0..7e56f08 100644 --- a/translations/en.toml +++ b/translations/en.toml @@ -401,4 +401,4 @@ To join a group, talk to %s and write in a private message `/join `. # DALLE GENERATE generateDalleHelpMessage = """Generate images using OpenAI DALLE 2.\nUsage: `/generate `\nPrice: 1000 sat""" generateDallePayInvoiceMessage = """Pay this invoice to generate four images πŸ‘‡""" -generateDalleGeneratingMessage = """Your images are being generated...""" \ No newline at end of file +generateDalleGeneratingMessage = """Your images are being generated. Please wait...""" \ No newline at end of file From 52ac3bfc132b680da9dd7ae3bca65ee11df40472 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 30 Aug 2022 18:06:16 +0200 Subject: [PATCH 13/13] fix kraut --- translations/de.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translations/de.toml b/translations/de.toml index 9d27036..2c71b6f 100644 --- a/translations/de.toml +++ b/translations/de.toml @@ -124,8 +124,8 @@ advancedMessage = """%s */faucet* 🚰 Erzeuge einen Zapfhahn: `/faucet ` */tipjar* 🍯 Erzeuge eine Spendendose: `/tipjar ` */group* 🎟 Tickets fΓΌr Gruppenchats: `/group add []` -*/shop* πŸ› Durchsuche shops: `/shop` oder `/shop ` -*/generate* πŸŽ† Generiere bilder mit DALLE-2: `/generate `""" +*/shop* πŸ› Durchsuche Shops: `/shop` oder `/shop ` +*/generate* πŸŽ† Generiere Bilder mit DALLE-2: `/generate `""" # GENERIC enterAmountRangeMessage = """πŸ’― Gebe Betrag zwuschen %d und %d sat ein."""