Skip to content

Commit

Permalink
Merge pull request #26 from claustra01/feat/image-message
Browse files Browse the repository at this point in the history
Feat/image message
  • Loading branch information
claustra01 authored Apr 15, 2024
2 parents 06979c4 + b4cb15d commit b256afb
Show file tree
Hide file tree
Showing 13 changed files with 642 additions and 85 deletions.
2 changes: 2 additions & 0 deletions bot/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ LIFF_URL=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT=

OPENAI_API_KEY=
21 changes: 21 additions & 0 deletions bot/db/cruds.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,24 @@ func UpdateRefreshToken(id string, refreshToken string) error {

return err
}

func GetRefreshToken(id string) (string, error) {
if id == "" {
return "", errors.New("id must not be empty")
}

query := `
SELECT refresh_token FROM users WHERE id = $1;
`
row := DB.QueryRow(query, id)

var refreshToken string
err := row.Scan(&refreshToken)
if err == sql.ErrNoRows {
return "", ErrNoRecord
} else if err != nil {
return "", err
}

return refreshToken, nil
}
106 changes: 106 additions & 0 deletions bot/google/calendar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package google

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)

type CalendarContentInterface interface {
GetType() string
}

func (c CalendarContent) GetType() string {
return c.Type
}

type CalendarContent struct {
Type string `json:"type"`
Summary string `json:"summary"`
Location string `json:"location"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
}

type CalenderEvent struct {
Summary string `json:"summary"`
Location string `json:"location"`
Start EventDateTime `json:"start"`
End EventDateTime `json:"end"`
ColorId string `json:"colorId"`
}

type EventDateTime struct {
DateTime time.Time `json:"dateTime"`
TimeZone string `json:"timeZone"`
}

func ParseCalendarContent(jsonStr string) (CalendarContent, error) {
var c CalendarContent
err := json.Unmarshal([]byte(jsonStr), &c)
if err != nil {
return c, err
}

if c.Type == "unknown" {
return c, errors.New("Calendar content type is unknown")
}

return c, nil
}

func EventTypeToColorId(eventType string) string {
switch eventType {
case "event":
return "7"
case "work":
return "5"
case "reminder":
return "11"
default:
return "8"
}
}

func (c *OAuthClient) RegisterCalenderEvent(content CalendarContent, accessToken string) error {
event := CalenderEvent{
Summary: content.Summary,
Location: content.Location,
Start: EventDateTime{
DateTime: content.Start,
TimeZone: content.Start.Location().String(),
},
End: EventDateTime{
DateTime: content.End,
TimeZone: content.End.Location().String(),
},
ColorId: EventTypeToColorId(content.Type),
}

eventJson, err := json.Marshal(event)
if err != nil {
return err
}

req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, c.Config.CalenderEndpoint+"/calendars/primary/events", bytes.NewBuffer(eventJson))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+accessToken)

resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Failed to register event: %s", resp.Status)
}

return nil
}
45 changes: 22 additions & 23 deletions bot/google/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,43 @@ package google

import (
"context"
"net/http"
"os"
)

var (
ClientId = os.Getenv("GOOGLE_CLIENT_ID")
ClientSecret = os.Getenv("GOOGLE_CLIENT_SECRET")
Scopes = []string{"https://www.googleapis.com/auth/calendar"}
Endpoint = "https://accounts.google.com/o/oauth2"
RedirectUri = os.Getenv("GOOGLE_REDIRECT")
)

type OAuthClientInterface interface {
GetToken(code string) (string, error)
}

type OAuthClient struct {
OAuthClientInterface
Config *Config
ctx context.Context
httpClient *http.Client
Config *Config
ctx context.Context
}

type Config struct {
ClientId string
ClientSecret string
Scopes []string
Endpoint string
RedirectUri string
ClientId string
ClientSecret string
Scopes []string
Endpoint string
CalenderEndpoint string
RedirectUri string
}

func NewOAuthClient(ctx context.Context) *OAuthClient {
config := &Config{
ClientId: os.Getenv("GOOGLE_CLIENT_ID"),
ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
Scopes: []string{"https://www.googleapis.com/auth/calendar"},
Endpoint: "https://oauth2.googleapis.com/token",
CalenderEndpoint: "https://www.googleapis.com/calendar/v3",
RedirectUri: os.Getenv("GOOGLE_REDIRECT"),
}

return &OAuthClient{
Config: &Config{
ClientId: ClientId,
ClientSecret: ClientSecret,
Scopes: Scopes,
Endpoint: Endpoint,
RedirectUri: RedirectUri,
},
ctx: ctx,
httpClient: http.DefaultClient,
Config: config,
ctx: ctx,
}
}
64 changes: 56 additions & 8 deletions bot/google/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@ package google

import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
)

type TokenRequestBody struct {
type RefreshTokenRequest struct {
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectUri string `json:"redirect_uri"`
GrantType string `json:"grant_type"`
Code string `json:"code"`
}

type TokenResponseBody struct {
type AccessTokenRequest struct {
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RefreshToken string `json:"refresh_token"`
GrantType string `json:"grant_type"`
}

type TokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
RefreshToken string `json:"refresh_token,omitempty"`
Scope string `json:"scope"`
TokenType string `json:"token_type"`
}

func (c *OAuthClient) GetRefreshToken(code string) (string, error) {

reqBody := TokenRequestBody{
reqBody := RefreshTokenRequest{
ClientId: c.Config.ClientId,
ClientSecret: c.Config.ClientSecret,
RedirectUri: c.Config.RedirectUri,
Expand All @@ -37,14 +45,14 @@ func (c *OAuthClient) GetRefreshToken(code string) (string, error) {
return "", err
}

req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, c.Config.Endpoint+"/token", bytes.NewBuffer(reqJson))
// TODO: Clone context
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, c.Config.Endpoint, bytes.NewBuffer(reqJson))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
}
Expand All @@ -55,11 +63,51 @@ func (c *OAuthClient) GetRefreshToken(code string) (string, error) {
return "", err
}

var respBody TokenResponseBody
var respBody TokenResponse
err = json.Unmarshal(body, &respBody)
if err != nil {
return "", err
}

return respBody.RefreshToken, nil
}

func (c *OAuthClient) GetAccessToken(refreshToken string) (string, error) {

reqBody := AccessTokenRequest{
ClientId: c.Config.ClientId,
ClientSecret: c.Config.ClientSecret,
RefreshToken: refreshToken,
GrantType: "refresh_token",
}
reqJson, err := json.Marshal(reqBody)
if err != nil {
return "", err
}

// TODO: Clone context
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, c.Config.Endpoint, bytes.NewBuffer(reqJson))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}

var respBody TokenResponse
err = json.Unmarshal(body, &respBody)
if err != nil {
return "", err
}

return respBody.AccessToken, nil
}
Loading

0 comments on commit b256afb

Please sign in to comment.