diff --git a/.gitignore b/.gitignore
index f53e225..5859cfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Server builds
server/rpgpt
+server/*.db
# sensitive
API_KEY.bat
diff --git a/build_web_debug.bat b/build_web_debug.bat
index fa498fd..81d0a09 100644
--- a/build_web_debug.bat
+++ b/build_web_debug.bat
@@ -9,8 +9,10 @@ call run_codegen.bat || goto :error
@REM GO FUCK YOURSELF
set FLAGS=-s TOTAL_STACK=5242880
+copy marketing_page\favicon.ico build_web\favicon.ico
+
@echo on
-emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -O0 -s ALLOW_MEMORY_GROWTH %FLAGS% --source-map-base ../ -gsource-map -DDEVTOOLS -Ithirdparty -Igen main.c -o build_web\index.html --preload-file assets --shell-file web_template.html || goto :error
+emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input,_stop_controlling_input,_start_controlling_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -O0 -s ALLOW_MEMORY_GROWTH %FLAGS% --source-map-base ../ -gsource-map -DDEVTOOLS -Ithirdparty -Igen main.c -o build_web\index.html --preload-file assets --shell-file web_template.html || goto :error
@echo off
goto :EOF
diff --git a/build_web_release.bat b/build_web_release.bat
index 527ead8..8a9934f 100644
--- a/build_web_release.bat
+++ b/build_web_release.bat
@@ -8,8 +8,12 @@ call run_codegen.bat || goto :error
@REM GO FUCK YOURSELF
set FLAGS=-s TOTAL_STACK=5242880
+copy marketing_page\favicon.ico build_web_release\favicon.ico
+copy marketing_page\eye_closed.svg build_web_release\eye_closed.svg
+copy marketing_page\eye_open.svg build_web_release\eye_open.svg
+
echo Building release
-emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -DNDEBUG -O2 -s ALLOW_MEMORY_GROWTH %FLAGS% -Ithirdparty -Igen main.c -o build_web_release\index.html --preload-file assets --shell-file web_template.html || goto :error
+emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input,_stop_controlling_input,_start_controlling_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -DNDEBUG -O2 -s ALLOW_MEMORY_GROWTH %FLAGS% -Ithirdparty -Igen main.c -o build_web_release\index.html --preload-file assets --shell-file web_template.html || goto :error
goto :EOF
diff --git a/main.c b/main.c
index a369ec8..e571d82 100644
--- a/main.c
+++ b/main.c
@@ -354,10 +354,19 @@ void begin_text_input()
}
#else
#ifdef WEB
-void begin_text_input()
+EMSCRIPTEN_KEEPALIVE
+void stop_controlling_input()
{
- Log("Disabling event handlers\n");
_sapp_emsc_unregister_eventhandlers(); // stop getting input, hand it off to text input
+}
+
+EMSCRIPTEN_KEEPALIVE
+void start_controlling_input()
+{
+ _sapp_emsc_register_eventhandlers();
+}
+void begin_text_input()
+{
memset(keydown, 0, ARRLEN(keydown));
emscripten_run_script("start_dialog();");
}
@@ -747,9 +756,13 @@ void begin_text_input(); // called when player engages in dialog, must say somet
// a callback, when 'text backend' has finished making text. End dialog
void end_text_input(char *what_player_said)
{
+ // avoid double ending text input
+ if(player->state != CHARACTER_TALKING)
+ {
+ return;
+ }
player->state = CHARACTER_IDLE;
#ifdef WEB // hacky
- _sapp_emsc_register_eventhandlers();
#endif
size_t player_said_len = strlen(what_player_said);
@@ -1115,6 +1128,11 @@ void audio_stream_callback(float *buffer, int num_frames, int num_channels)
void init(void)
{
+#ifdef WEB
+ EM_ASM({
+ set_server_url(UTF8ToString($0));
+ }, SERVER_URL);
+#endif
Log("Size of entity struct: %zu\n", sizeof(Entity));
Log("Size of %d entities: %zu kb\n", (int)ARRLEN(entities), sizeof(entities)/1024);
sg_setup(&(sg_desc){
diff --git a/marketing_page/favicon.ico b/marketing_page/favicon.ico
new file mode 100644
index 0000000..d3d4df6
Binary files /dev/null and b/marketing_page/favicon.ico differ
diff --git a/marketing_page/index.html b/marketing_page/index.html
index fe101b6..4be3d6b 100644
--- a/marketing_page/index.html
+++ b/marketing_page/index.html
@@ -1,4 +1,5 @@
+
PlayGPT
diff --git a/run_codegen.bat b/run_codegen.bat
index a0c0b40..3575ce1 100644
--- a/run_codegen.bat
+++ b/run_codegen.bat
@@ -4,6 +4,7 @@ echo Asset packs which must be bought and unzipped into root directory before ru
echo https://rafaelmatos.itch.io/epic-rpg-world-pack-ancient-ruins
echo https://sventhole.itch.io/undead-pixel-art-characters
+
rmdir /S /q assets\copyrighted
mkdir assets\copyrighted
copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Characters\NPC Merchant-idle.png" "assets\copyrighted\merchant.png" || goto :error
diff --git a/server/go.mod b/server/go.mod
index 2d1419c..1b13e21 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -2,4 +2,13 @@ module github.com/creikey/rpgpt
go 1.19
-require github.com/sashabaranov/go-gpt3 v1.2.1 // indirect
+require (
+ github.com/jinzhu/inflection v1.0.0 // indirect
+ github.com/jinzhu/now v1.1.5 // indirect
+ github.com/mattn/go-sqlite3 v1.14.15 // indirect
+ github.com/sashabaranov/go-gpt3 v1.2.1 // indirect
+ github.com/stripe/stripe-go/v72 v72.122.0 // indirect
+ github.com/stripe/stripe-go/v74 v74.13.0 // indirect
+ gorm.io/driver/sqlite v1.4.4 // indirect
+ gorm.io/gorm v1.24.6 // indirect
+)
diff --git a/server/go.sum b/server/go.sum
index db2116d..06fbc8f 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -1,2 +1,37 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
+github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sashabaranov/go-gpt3 v1.2.1 h1:kfU+vQ1ThI7p+xfwwJC8olEEEWjK3smgKZ3FcYbaLRQ=
github.com/sashabaranov/go-gpt3 v1.2.1/go.mod h1:BIZdbwdzxZbCrcKGMGH6u2eyGe1xFuX9Anmh3tCP8lQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stripe/stripe-go/v72 v72.122.0 h1:eRXWqnEwGny6dneQ5BsxGzUCED5n180u8n665JHlut8=
+github.com/stripe/stripe-go/v72 v72.122.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0=
+github.com/stripe/stripe-go/v74 v74.13.0 h1:n9VIeApHaGsqRQcEsr8ANldfFrLzFSasfNBkq0roPTw=
+github.com/stripe/stripe-go/v74 v74.13.0/go.mod h1:f9L6LvaXa35ja7eyvP6GQswoaIPaBRvGAimAO+udbBw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
+gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
+gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
+gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
+gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
diff --git a/server/main.go b/server/main.go
index 08147b1..3965eec 100644
--- a/server/main.go
+++ b/server/main.go
@@ -2,31 +2,316 @@ package main
import (
"fmt"
+ "time"
"net/http"
"io"
"os"
"context"
"log"
+ "io/ioutil"
+ "strings"
+ "encoding/json"
+ "math/rand"
gogpt "github.com/sashabaranov/go-gpt3"
+ "github.com/stripe/stripe-go/v74"
+ "github.com/stripe/stripe-go/v74/webhook"
+ "github.com/stripe/stripe-go/v74/checkout/session"
+
+ "gorm.io/gorm"
+ "gorm.io/driver/sqlite"
+
+)
+
+// BoughtType values. do not reorganize these or you fuck up the database
+const (
+ DayPass = iota
+)
+
+const (
+ // A-Z and 0-9, four digits means this many codes
+ MaxCodes = 36 * 36 * 36 * 36
)
+type userCode int
+
+type User struct {
+ CreatedAt time.Time
+ UpdatedAt time.Time
+ DeletedAt gorm.DeletedAt `gorm:"index"`
+
+ Code userCode `gorm:"primaryKey"` // of maximum value max codes, incremented one by one. These are converted to 4 digit alphanumeric code users can remember/use
+ BoughtTime int64 // unix time. Used to figure out if the pass is still valid
+ BoughtType int // enum
+
+ IsFulfilled bool // before users are checked out they are unfulfilled
+ CheckoutSessionID string
+}
+
var c *gogpt.Client
+var logResponses = false
+var doCors = false
+var checkoutRedirectTo string
+var daypassPriceId string
+var webhookSecret string
+var db *gorm.DB
+
+func intPow(n, m int) int {
+ if m == 0 {
+ return 1
+ }
+ result := n
+ for i := 2; i <= m; i++ {
+ result *= n
+ }
+ return result
+}
+
+var numberToChar = [...]rune{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+
+func codeToString(code userCode) (string, error) {
+ toReturn := ""
+ value := int(code)
+ // converting to base 36 (A-Z + numbers) then into characters, then appending
+ for digit := 3; digit >= 0; digit-- {
+ currentPlaceValue := value / intPow(36, digit)
+ value -= currentPlaceValue * intPow(36, digit)
+ if currentPlaceValue >= len(numberToChar) { return "", fmt.Errorf("Failed to generate usercode %d to string, currentPlaceValue %d and length of number to char %d", code, currentPlaceValue, len(numberToChar)) }
+ toReturn += string(numberToChar[currentPlaceValue])
+ }
+ return toReturn, nil;
+}
+
+func parseUserCode(s string) (userCode, error) {
+ asRune := []rune(s)
+ if len(asRune) != 4 { return 0, fmt.Errorf("String to deconvert is not of length 4: %s", s) }
+ var toReturn userCode = 0
+ for digit := 3; digit >= 0; digit-- {
+ curDigitNum := 0
+ found := false
+ for i, letter := range numberToChar {
+ if letter == asRune[digit] {
+ curDigitNum = i
+ found = true
+ }
+ }
+ if !found { return 0, fmt.Errorf("Failed to find digit's number %s", s) }
+ toReturn += userCode(curDigitNum * intPow(36, digit))
+ }
+ return toReturn, nil
+}
+
+func isUserOld(user User) bool {
+ return (currentTime() - user.BoughtTime) > 24*60*60
+}
+
+func clearOld(db *gorm.DB) {
+ var users []User
+ result := db.Find(&users)
+ if result.Error != nil {
+ log.Fatal(result.Error)
+ }
+ var toDelete []userCode // codes
+ for _, user := range users {
+ if user.BoughtType != 0 {
+ panic("Don't know how to handle bought type " + string(user.BoughtType) + " yet")
+ }
+ if isUserOld(user) {
+ toDelete = append(toDelete, user.Code)
+ }
+ }
+
+ for _, del := range toDelete {
+ db.Delete(&User{}, del)
+ }
+}
+
+func webhookResponse(w http.ResponseWriter, req *http.Request) {
+ const MaxBodyBytes = int64(65536)
+ req.Body = http.MaxBytesReader(w, req.Body, MaxBodyBytes)
+
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ log.Printf("Error reading request body: %v\n", err)
+ w.WriteHeader(http.StatusServiceUnavailable)
+ return
+ }
+
+ endpointSecret := webhookSecret
+ event, err := webhook.ConstructEvent(body, req.Header.Get("Stripe-Signature"), endpointSecret)
+
+ if err != nil {
+ log.Printf("Error verifying webhook signature %s\n", err)
+ w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature
+ return
+ }
+
+ if event.Type == "checkout.session.completed" {
+ var session stripe.CheckoutSession
+ err := json.Unmarshal(event.Data.Raw, &session)
+ if err != nil {
+ log.Printf("Error parsing webhook JSON %s", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ params := &stripe.CheckoutSessionParams{}
+ params.AddExpand("line_items")
+
+ // Retrieve the session. If you require line items in the response, you may include them by expanding line_items.
+ // Fulfill the purchase...
+
+
+ var toFulfill User
+ found := false
+ for trial := 0; trial < 5; trial++ {
+ if db.Where("checkout_session_id = ?", session.ID).First(&toFulfill).Error != nil {
+ log.Println("Failed to fulfill user with ID " + session.ID)
+ } else {
+ found = true
+ break
+ }
+ }
+ if !found {
+ log.Println("Error Failed to find user in database to fulfill: very bad! ID: " + session.ID)
+ } else {
+ userString, err := codeToString(toFulfill.Code)
+ if err != nil {
+ log.Printf("Error strange thing, saved user's code was unable to be converted to a string %s", err)
+ }
+ log.Printf("Fulfilling user with code %s\n", userString)
+ toFulfill.IsFulfilled = true
+ db.Save(&toFulfill)
+ }
+ }
+
+ w.WriteHeader(http.StatusOK)
+}
+
+func checkout(w http.ResponseWriter, req *http.Request) {
+ if doCors {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ }
+
+ // generate a code
+ var newCode string
+ var newCodeUser userCode
+ found := false
+ for i := 0; i < 1000; i++ {
+ codeInt := rand.Intn(MaxCodes)
+ newCodeUser = userCode(codeInt)
+ var tmp User
+ r := db.Where("Code = ?", newCodeUser).Limit(1).Find(&tmp)
+ if r.RowsAffected == 0{
+ var err error
+ newCode, err = codeToString(newCodeUser)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Fatalf("Failed to generate code from random number: %s", err)
+ return
+ }
+ found = true
+ break
+ }
+ }
+ if !found {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Fatal("Failed to find new code!!!")
+ return
+ }
+
+ params := &stripe.CheckoutSessionParams {
+ LineItems: []*stripe.CheckoutSessionLineItemParams {
+ &stripe.CheckoutSessionLineItemParams{
+ Price: stripe.String(daypassPriceId),
+ Quantity: stripe.Int64(1),
+ },
+ },
+ Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
+ SuccessURL: stripe.String(checkoutRedirectTo),
+ CancelURL: stripe.String(checkoutRedirectTo),
+ }
+
+ s, err := session.New(params)
+
+ if err != nil {
+ log.Printf("session.New: %v", err)
+ }
+
+ log.Printf("Creating user with checkout session ID %s\n", s.ID)
+ result := db.Create(&User {
+ Code: newCodeUser,
+ BoughtTime: currentTime(),
+ BoughtType: DayPass,
+ IsFulfilled: false,
+ CheckoutSessionID: s.ID,
+ })
+ if result.Error != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Printf("Failed to write to database: %s", result.Error)
+ } else {
+ fmt.Fprintf(w, "%s|%s", newCode, s.URL)
+ }
+}
func index(w http.ResponseWriter, req *http.Request) {
- //time.Sleep(4 * time.Second)
req.Body = http.MaxBytesReader(w, req.Body, 1024 * 1024) // no sending huge files to crash the server
- promptBytes, err := io.ReadAll(req.Body)
+ if doCors {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ }
+ bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
+ log.Println("Bad error: ", err)
return
} else {
- promptString := string(promptBytes)
+ bodyString := string(bodyBytes)
+ splitBody := strings.Split(bodyString, "|")
+
+ if len(splitBody) != 2 {
+ w.WriteHeader(http.StatusBadRequest)
+ }
+ var promptString string = splitBody[1]
+ var userToken string = splitBody[0]
- fmt.Println()
- fmt.Println("Println line prompt string: ", promptString)
+ // see if need to pay
+ rejected := false
+ {
+ if len(userToken) != 4 {
+ log.Println("Rejected because not 4: `" + userToken + "`")
+ rejected = true
+ } else {
+ var thisUser User
+ thisUserCode, err := parseUserCode(userToken)
+ if err != nil {
+ log.Printf("Error: Failed to parse user token %s\n", userToken)
+ rejected = true
+ } else {
+ if db.First(&thisUser, thisUserCode).Error != nil {
+ log.Printf("User code %d string %s couldn't be found in the database: %s\n", thisUserCode, userToken, db.Error)
+ rejected = true
+ } else {
+ if isUserOld(thisUser) {
+ log.Println("User code " + userToken + " is old, not valid")
+ db.Delete(&thisUser)
+ rejected = true
+ } else {
+ rejected = false
+ }
+ }
+ }
+ }
+ }
+ if rejected {
+ fmt.Fprintf(w, "0")
+ return
+ }
+
+ if logResponses {
+ log.Println("Println line prompt string: ", promptString)
+ }
ctx := context.Background()
- req := gogpt.CompletionRequest{
+ req := gogpt.CompletionRequest {
Model: "curie:ft-personal-2023-03-24-03-06-24",
MaxTokens: 80,
Prompt: promptString,
@@ -39,26 +324,64 @@ func index(w http.ResponseWriter, req *http.Request) {
}
resp, err := c.CreateCompletion(ctx, req)
if err != nil {
- fmt.Println("Failed to generate: ", err)
+ log.Println("Error Failed to generate: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
response := resp.Choices[0].Text
- fmt.Println("Println response: ", response)
- fmt.Fprintf(w, "%s", response)
+ if logResponses {
+ log.Println("Println response: ", response)
+ }
+ fmt.Fprintf(w, "1%s", response)
}
}
+func currentTime() int64 {
+ return time.Now().Unix()
+}
+
func main() {
+ var err error
+ db, err = gorm.Open(sqlite.Open("rpgpt.db"), &gorm.Config{})
+ if err != nil {
+ log.Fatal(err)
+ }
+ db.AutoMigrate(&User{})
+
+ clearOld(db)
+
api_key := os.Getenv("OPENAI_API_KEY")
if api_key == "" {
log.Fatal("Must provide openai key")
}
+ checkoutRedirectTo = os.Getenv("REDIRECT_TO")
+ if checkoutRedirectTo == "" {
+ log.Fatal("Must provide a base URL (without slash) for playgpt to redirect to")
+ }
+ stripeKey := os.Getenv("STRIPE_KEY")
+ if stripeKey == "" {
+ log.Fatal("Must provide stripe key")
+ }
+ daypassPriceId = os.Getenv("PRICE_ID")
+ if daypassPriceId == "" {
+ log.Fatal("Must provide daypass price ID")
+ }
+ stripe.Key = stripeKey
+ webhookSecret = os.Getenv("WEBHOOK_SECRET")
+ if webhookSecret == "" {
+ log.Fatal("Must provide webhook secret for receiving checkout completed events")
+ }
+
+ logResponses = os.Getenv("LOG_RESPONSES") != ""
+ doCors = os.Getenv("CORS") != ""
c = gogpt.NewClient(api_key)
http.HandleFunc("/", index)
+ http.HandleFunc("/webhook", webhookResponse)
+ http.HandleFunc("/checkout", checkout)
- log.Println("Serving...")
- http.ListenAndServe(":8090", nil)
+ portString := ":8090"
+ log.Println("Serving on " + portString + "...")
+ http.ListenAndServe(portString, nil)
}
diff --git a/todo.txt b/todo.txt
index 2bb0417..ca4d517 100644
--- a/todo.txt
+++ b/todo.txt
@@ -6,6 +6,7 @@ Happening by END OF STREAM:
- New characters/items from fate
- New art in
- Old man in beginning is invincible
+ - Make new openai key (it was leaked)
- Add cancel button
- Style buttons
- Make map better
diff --git a/web_template.html b/web_template.html
index 1d12f4c..ef3c7a7 100644
--- a/web_template.html
+++ b/web_template.html
@@ -1,36 +1,16 @@
-
-
+
+
+
AI RPG
+
+
-
+
+
+
+
@@ -109,7 +175,6 @@ body {
-
{{{ SCRIPT }}}