ifcurrentPlaceValue>=len(numberToChar){return"",fmt.Errorf("Failed to generate usercode %d to string, currentPlaceValue %d and length of number to char %d in place %d",code,currentPlaceValue,len(numberToChar),place)}
toReturn[index]=numberToChar[currentPlaceValue]
}
returnstring(toReturn[:]),nil
}
funcParseUserCode(sstring)(UserCode,error){
asRune:=[]rune(s)
iflen(asRune)!=4{return0,fmt.Errorf("String to deconvert is not of length 4: %s",s)}
vartoReturnUserCode=0
forplace:=3;place>=0;place--{
index:=3-place
curDigitNum:=0
found:=false
fori,letter:=rangenumberToChar{
ifletter==asRune[index]{
curDigitNum=i
found=true
}
}
if!found{return0,fmt.Errorf("Failed to find place's number %s",s)}
// BoughtType values. do not reorganize these or you fuck up the database
// BoughtType values. do not reorganize these or you fuck up the database
@ -32,14 +33,13 @@ const (
MaxCodes=36*36*36*36
MaxCodes=36*36*36*36
)
)
typeuserCodeint
typeUserstruct{
typeUserstruct{
CreatedAttime.Time
CreatedAttime.Time
UpdatedAttime.Time
UpdatedAttime.Time
DeletedAtgorm.DeletedAt`gorm:"index"`
DeletedAtgorm.DeletedAt`gorm:"index"`
CodeuserCode `gorm:"primaryKey"`// of maximum value max codes, incremented one by one. These are converted to 4 digit alphanumeric code users can remember/use
Codecodes.UserCode `gorm:"primaryKey"`// of maximum value max codes, incremented one by one. These are converted to 4 digit alphanumeric code users can remember/use
BoughtTimeint64// unix time. Used to figure out if the pass is still valid
BoughtTimeint64// unix time. Used to figure out if the pass is still valid
BoughtTypeint// enum
BoughtTypeint// enum
@ -54,52 +54,24 @@ var checkoutRedirectTo string
vardaypassPriceIdstring
vardaypassPriceIdstring
varwebhookSecretstring
varwebhookSecretstring
vardb*gorm.DB
vardb*gorm.DB
vardaypassTimedOut=make(map[codes.UserCode]int64)// value is time last requested, rate limiting by day pass. If exists is rate limited, should be removed when ok to request again
// for 10 free minutes a day, is when ip address began requesting
// converting to base 36 (A-Z + numbers) then into characters, then appending
fordigit:=3;digit>=0;digit--{
currentPlaceValue:=value/intPow(36,digit)
value-=currentPlaceValue*intPow(36,digit)
ifcurrentPlaceValue>=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])
}
}
returntoReturn,nil;
}
funcparseUserCode(sstring)(userCode,error){
asRune:=[]rune(s)
iflen(asRune)!=4{return0,fmt.Errorf("String to deconvert is not of length 4: %s",s)}
vartoReturnuserCode=0
fordigit:=3;digit>=0;digit--{
curDigitNum:=0
found:=false
fori,letter:=rangenumberToChar{
ifletter==asRune[digit]{
curDigitNum=i
found=true
}
}
if!found{return0,fmt.Errorf("Failed to find digit's number %s",s)}
toReturn+=userCode(curDigitNum*intPow(36,digit))
}
}
returntoReturn,nil
}
}
funcisUserOld(userUser)bool{
funcisUserOld(userUser)bool{
return(currentTime()-user.BoughtTime)>24*60*60
return(currentTime()-user.BoughtTime)>24*60*60
}
}
@ -110,7 +82,7 @@ func clearOld(db *gorm.DB) {
ifresult.Error!=nil{
ifresult.Error!=nil{
log.Fatal(result.Error)
log.Fatal(result.Error)
}
}
vartoDelete[]userCode // codes
vartoDelete[]codes.UserCode // codes
for_,user:=rangeusers{
for_,user:=rangeusers{
ifuser.BoughtType!=0{
ifuser.BoughtType!=0{
panic("Don't know how to handle bought type "+string(user.BoughtType)+" yet")
panic("Don't know how to handle bought type "+string(user.BoughtType)+" yet")
log.Println("Rejected because not 4: `"+userToken+"`")
// where I do the IP rate limiting
rejected=true
userKey:=req.RemoteAddr
createdTime,ok:=ipAddyTenFree[userKey]
if!ok{
ipAddyTenFree[userKey]=currentTime()
rejected=false
}else{
ifcurrentTime()-createdTime<10*60{
rejected=false
}else{
rejected=true// out of free time buddy
}
}
}else{
}else{
varthisUserUser
varthisUserUser
thisUserCode,err:=parseUserCode(userToken)
thisUserCode,err:=codes.ParseUserCode(userToken)
iferr!=nil{
iferr!=nil{
log.Printf("Error: Failed to parse user token %s\n",userToken)
log.Printf("Error: Failed to parse user token %s\n",userToken)
rejected=true
rejected=true
}else{
}else{
ifdb.First(&thisUser,thisUserCode).Error!=nil{
err:=db.First(&thisUser,thisUserCode).Error
log.Printf("User code %d string %s couldn't be found in the database: %s\n",thisUserCode,userToken,db.Error)
iferr!=nil{
log.Printf("User code %d string %s couldn't be found in the database: %s\n",thisUserCode,userToken,err)
rejected=true
rejected=true
}else{
}else{
ifisUserOld(thisUser){
ifisUserOld(thisUser){
log.Println("User code "+userToken+" is old, not valid")
log.Println("User code "+userToken+" is old, not valid")
db.Delete(&thisUser)
db.Delete(&thisUser)
rejected=true
rejected=true
}else{
// now have valid user, in the database, to be rate limit checked
// rate limiting based on user token
_,exists:=daypassTimedOut[thisUserCode]
ifexists{
rejected=true
}else{
}else{
rejected=false
rejected=false
daypassTimedOut[thisUserCode]=currentTime()
}
}
}
}
}
}
}
@ -381,7 +377,7 @@ func main() {
http.HandleFunc("/checkout",checkout)
http.HandleFunc("/checkout",checkout)
portString:=":8090"
portString:=":8090"
log.Println("Serving on " +portString+"...")
log.Println("DO NOT RUN WITH CLOUDFLARE PROXY it rate limits based on IP, if behind proxy every IP will be the same. Would need to fix req.RemoteAddr. Serving on " +portString+"...")
<h1>AI is expensive so you need to pay for it</h1>
<h1>AI is expensive so you need to pay for it</h1>
<p>You can play for free 10 minutes per day</p>
<p>You can play free 10 minutes a day. If you're seeing this you must buy a day pass to continue. If you already bought one, but are still seeing this, check that the code matches up. If you're unsatisfied with your service, you can get a refund via the email stripe sent you.</p>