upgrade to .todo. inbox

master v0.17
Bel LaPointe 2023-11-09 08:24:08 -07:00
parent 6a70c6d2ac
commit e3d821e219
6 changed files with 69 additions and 163 deletions

View File

@ -1,15 +1,11 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"gogs.inhome.blapointe.com/local/args"
"gogs.inhome.blapointe.com/local/oauth2"
"gogs.inhome.blapointe.com/local/storage"
"log"
"net/http"
"strings"
"gitea.inhome.blapointe.com/local/args"
"gitea.inhome.blapointe.com/local/storage"
)
type Uploader int
@ -124,64 +120,9 @@ func NewConfig() Config {
}
func getToken(as *args.ArgSet) string {
c := &http.Client{CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}}
body := "username=" + as.GetString("todopass")
name := strings.Split(as.GetString("todoaddr"), ".")[0]
name = strings.TrimPrefix(name, "http://")
name = strings.TrimPrefix(name, "https://")
req, err := http.NewRequest("POST", as.GetString("authaddr")+"/authorize/"+name+"?"+oauth2.REDIRECT+"=127.0.0.1", strings.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := c.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode > 399 {
panic("bad status getting token: " + resp.Status)
}
cookie := resp.Header.Get("Set-Cookie")
token := cookie[strings.Index(cookie, "=")+1:]
token = strings.Split(token, "; ")[0]
if len(token) == 0 {
panic(fmt.Sprintf("no token found: (%v) %v", resp.StatusCode, resp.Header))
}
return token
panic("DEAD")
}
func getList(as *args.ArgSet, token string) string {
req, err := http.NewRequest("GET", as.GetString("todoaddr")+"/ajax.php?loadLists", nil)
if err != nil {
panic(err)
}
req.Header.Set("Cookie", oauth2.COOKIE+"="+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var r struct {
List []struct {
ID string `json:"id"`
} `json:"list"`
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
if err := json.Unmarshal(b, &r); err != nil {
panic(fmt.Errorf("%v: %s", err, b))
}
if len(r.List) == 0 {
panic("no lists found")
}
list := r.List[0].ID
if len(list) == 0 {
panic("empty list found")
}
return list
panic("DEAD")
}

15
go.mod
View File

@ -1,18 +1,17 @@
module gogs.inhome.blapointe.com/local/email-xactions-to-todo
module gitea.inhome.blapointe.com/local/email-xactions-to-todo
go 1.17
require (
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d
github.com/google/uuid v1.3.0
gogs.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20230410170022-4b1b19400094
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34
gogs.inhome.blapointe.com/local/oauth2 v0.0.0-20230410162733-d39498ff8454
gogs.inhome.blapointe.com/local/storage v0.0.0-20230410162102-db39d7b02e29
gopkg.in/yaml.v2 v2.4.0
)
require (
cloud.google.com/go v0.33.1 // indirect
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc // indirect
github.com/Unknwon/goconfig v0.0.0-20181105214110-56bd8ab18619 // indirect
github.com/abbot/go-http-auth v0.4.0 // indirect
github.com/aws/aws-sdk-go v1.15.81 // indirect
@ -46,15 +45,15 @@ require (
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.mongodb.org/mongo-driver v1.7.2 // indirect
gogs.inhome.blapointe.com/local/logb v0.0.0-20230410154319-880efa39d871 // indirect
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
google.golang.org/api v0.0.0-20181120235003-faade3cbb06a // indirect
google.golang.org/appengine v1.3.0 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

22
go.sum
View File

@ -1,6 +1,14 @@
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.33.1 h1:fmJQWZ1w9PGkHR1YL/P7HloDvqlmKQ4Vpb7PC2e+aCk=
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a h1:vDt7kgsUwsI6fq7ObPUuUQ2CiIts3RaEsvcuwruspEY=
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a/go.mod h1:3LBm8MXwR5D5Z8gwp5p39KmabJR/F7cxBSZwVHWTfQQ=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56 h1:zTGGZ77KLFagqUvDSgTOnm0qF+iSLwQWiEtGjb2jjlY=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56/go.mod h1:SqCOE3bE3wvrztVIQGHuyxHKfDjRKU9EWhBdkmkiwyc=
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc h1:u3akQkq12V8xWXlcDgjZxIK6hqo6f1eHd9KOxAKMoKc=
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc/go.mod h1:KwilawX4UgD4HxSJAVFEzkuckrnHeQrd49KwUX6GpYU=
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d h1:SQq4hWImnvtrRfpPgOW4go+sBjMluuhRL/43b8L0yB4=
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d/go.mod h1:TRK5z/XTT6jws++Q21Y8DQot+5vZGTNeHf+RjuY8aQk=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-storage-blob-go v0.0.0-20181023070848-cf01652132cc/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@ -191,17 +199,6 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible/go.mod h1:w6wqLDQ5bBTzxGJ55581UrSwLrsTAsdo9N6yX/8d9RY=
go.mongodb.org/mongo-driver v1.7.2 h1:pFttQyIiJUHEn50YfZgC9ECjITMT44oiN36uArf/OFg=
go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
gogs.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20230410170022-4b1b19400094 h1:XaA0Pr+CsoDIpmwh6y/acNKRcBAp+HHh8Jnp1TFDrMw=
gogs.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20230410170022-4b1b19400094/go.mod h1:09XWh7o68eXoDrVd9RSQBZOLYUmR7nlA2P3779usmgA=
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34 h1:0tuX5dfOksiOQD1vbJjVNVTVxTTIng7UrUdSLF5T+Ao=
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34/go.mod h1:YG9n3Clg7683ohkVnJK2hdX8bBS9EojIsd1qPZumX0Y=
gogs.inhome.blapointe.com/local/logb v0.0.0-20230410154319-880efa39d871 h1:cMGPiwvK/QGg4TfW8VasO6SsS/O7UQmwyKDErV/ozoA=
gogs.inhome.blapointe.com/local/logb v0.0.0-20230410154319-880efa39d871/go.mod h1:E0pLNvMLzY0Kth1W078y+06z1AUyVMWnChMpRFf4w2Q=
gogs.inhome.blapointe.com/local/oauth2 v0.0.0-20230410162733-d39498ff8454 h1:U8gUhe9E97/uG3ne6D1VONCCVC6jjBbF1gDMKn3GCeo=
gogs.inhome.blapointe.com/local/oauth2 v0.0.0-20230410162733-d39498ff8454/go.mod h1:YDG4DAUbmKcQUDWdZAJyoUtX+N2zQIFQ0fz88lAPuiU=
gogs.inhome.blapointe.com/local/router v0.0.0-20230410162418-08ccdc13df87/go.mod h1:FCXhK6+lzJcxBsptnei6vw9pChuQvr4NtuosngjVJDk=
gogs.inhome.blapointe.com/local/storage v0.0.0-20230410162102-db39d7b02e29 h1:SPSz7yQsEfScqyLlBS5uNSOGeT203BkIkFCL8jrm/FA=
gogs.inhome.blapointe.com/local/storage v0.0.0-20230410162102-db39d7b02e29/go.mod h1:zk8Fe2Ezc2f6oOe2yllsbEhXqssUU1K2faoS0eQ9alY=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -241,9 +238,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -4,7 +4,7 @@ import (
"log"
"regexp"
"gogs.inhome.blapointe.com/local-sandbox/contact"
"gitea.inhome.blapointe.com/local-sandbox/contact"
)
func main() {

View File

@ -1,27 +1,20 @@
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"gogs.inhome.blapointe.com/local/oauth2"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
yaml "gopkg.in/yaml.v2"
"github.com/google/uuid"
)
func Upload(config Config, transaction *Transaction) error {
switch config.Uploader {
case UploaderTodo:
return uploadTodo(config, transaction)
panic("DEAD")
case UploaderLedger:
return uploadLedger(config, transaction)
case UploaderPTTodo:
@ -31,67 +24,14 @@ func Upload(config Config, transaction *Transaction) error {
}
}
func uploadTodo(config Config, transaction *Transaction) error {
params := url.Values{
"list": {config.TodoList},
"title": {transaction.Format()},
"tag": {config.TodoTag},
}
req, err := http.NewRequest("POST", config.TodoAddr+"/ajax.php?newTask", strings.NewReader(params.Encode()))
if err != nil {
return err
}
req.Header.Set("Cookie", oauth2.COOKIE+"="+config.TodoToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("bad status from todo: %v: %s", resp.StatusCode, b)
}
return nil
}
func uploadPTTodo(config Config, transaction *Transaction) error {
b, err := ioutil.ReadFile(config.TodoAddr)
if os.IsNotExist(err) {
b = []byte("todo:\n")
} else if err != nil {
return err
} else if len(b) == 0 {
b = []byte("todo:\n")
}
f, err := ioutil.TempFile(path.Dir(config.TodoAddr), path.Base("."+config.TodoAddr))
f, err := os.Create(fmt.Sprintf("%s.todo.%s", config.TodoAddr, uuid.New().String()))
if err != nil {
return err
}
defer f.Close()
sep := []byte{'\n'}
seek := []byte("todo:")
for len(b) > 0 {
idx := bytes.Index(b, sep)
if idx == -1 {
idx = len(b) - 1
}
fmt.Fprintf(f, "%s\n", b[:idx])
if bytes.Equal(bytes.TrimSpace(b[:idx]), seek) {
fmt.Fprintf(f, `- {"todo":%q, "tags":%q}%s`, transaction.Format(), config.TodoTag, "\n")
}
b = b[idx+1:]
}
f.Close()
var v interface{}
b, err = ioutil.ReadFile(f.Name())
if err != nil {
return err
}
if err := yaml.Unmarshal(b, &v); err != nil {
return err
}
return os.Rename(f.Name(), config.TodoAddr)
return f.Close()
}
func uploadLedger(config Config, transaction *Transaction) error {

View File

@ -3,17 +3,30 @@ package main
import (
"bytes"
"io/ioutil"
"gogs.inhome.blapointe.com/local/storage"
"os"
"path"
"path/filepath"
"testing"
"gitea.inhome.blapointe.com/local/storage"
"github.com/google/uuid"
)
func TestUploadPTTodo(t *testing.T) {
addr := path.Join(t.TempDir(), "test.upload.pttodo")
config := Config{TodoAddr: addr, TodoTag: "expense"}
reset := func(t *testing.T) {
files, err := filepath.Glob(addr + "*")
if err != nil {
t.Fatal(err)
}
for _, f := range files {
if f != addr {
os.Remove(f)
}
}
}
xaction := func() *Transaction {
return &Transaction{
ID: "id",
@ -24,6 +37,7 @@ func TestUploadPTTodo(t *testing.T) {
}
}
t.Run("full file", func(t *testing.T) {
defer reset(t)
if err := ioutil.WriteFile(addr, []byte(`
todo:
- first
@ -37,18 +51,18 @@ done: []
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if bytes.Compare(bytes.TrimSpace(b), bytes.TrimSpace([]byte(`
todo:
- {"todo":"(today) /UCCU: 1.23 @ vendor vendor", "tags":"expense"}
- first
- todo: second
scheduled: []
done: []
`))) != 0 {
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}
if bytes.Compare(bytes.TrimSpace(b), bytes.TrimSpace([]byte(`- {"todo":"(today) /UCCU: 1.23 @ vendor vendor", "tags":"expense"}`))) != 0 {
t.Errorf("full file came out wrong: got %s", b)
}
if !bytes.Contains(b, []byte(xaction().Format())) {
@ -57,12 +71,20 @@ done: []
t.Logf("%s", b)
})
t.Run("no file", func(t *testing.T) {
defer reset(t)
os.Remove(addr)
err := uploadPTTodo(config, xaction())
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}
@ -72,6 +94,7 @@ done: []
t.Logf("%s", b)
})
t.Run("empty file", func(t *testing.T) {
defer reset(t)
if err := ioutil.WriteFile(addr, []byte{}, os.ModePerm); err != nil {
t.Fatal(err)
}
@ -79,7 +102,14 @@ done: []
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}