Skip to content

Commit

Permalink
feat(scripting): update scripting functions
Browse files Browse the repository at this point in the history
- add markAsRead function
- add markAsToRead function
- remove fetch function
  • Loading branch information
ncarlier committed Sep 27, 2024
1 parent 46efdf4 commit d444a4f
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This script gives control over a set of features:
- Change the title of the article (`setTitle(string)`)
- Change the text of the article (`setText(string)`)
- Categorize the article (`setCategory(string)`)
- Mark article as read (`markAsRead()`)
- Mark article as to-read (`markAsToRead()`)
- Notify connected devices of the article (`sendNotification()`)
- Call an outgoing webhook (`triggerWebhook(string)`)
- Disable the global notification policy for this webhook (`disableGlobalNotification()`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Ce script donne la main sur un ensemble de fonctionnalités:
- Changer le titre de l'article (`setTitle(string)`)
- Changer le texte de l'article (`setText(string)`)
- Catégoriser l'article (`setCategory(string)`)
- Marquer l'article comme lu (`markAsRead()`)
- Marquer l'article comme à lire (`markAsToRead()`)
- Notifier l'article aux appareils connectés (`sendNotification()`)
- Appeler un webhook sortant (`triggerWebhook(string)`)
- Désactiver la politique de notification globale pour ce webhook (`disableGlobalNotification()`)
Expand Down
66 changes: 0 additions & 66 deletions internal/scripting/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,15 @@ package scripting

import (
"fmt"
"io"
"net/http"
"net/url"
"strings"

"github.com/ncarlier/readflow/pkg/defaults"
"github.com/skx/evalfilter/v2/object"
"golang.org/x/net/html/charset"
)

type fnType = func(args []object.Object) object.Object

var VOID = &object.Void{}
var NULL = &object.Null{}

const MAX_RESPONSE_SIZE = 1 << 20 // 1Mb

func ErrorString(err error) *object.String {
return &object.String{Value: err.Error()}
}
Expand Down Expand Up @@ -83,61 +75,3 @@ func (i *Interpreter) fnPrintf(args []object.Object) object.Object {
i.logger.Debug().Str("fn", "printf").Msg(out)
return VOID
}

// fnFetch is the implementation of our `fetch` function.
func (i *Interpreter) fnFetch(args []object.Object) object.Object {
// we expect 1 argument
if len(args) == 0 {
return NULL
}
// type-check
if args[0].Type() != object.STRING {
return NULL
}
// get the target URL
rawurl := args[0].(*object.String).Value
u, err := url.ParseRequestURI(rawurl)
if err != nil {
return ErrorString(err)
}
if u.Scheme != "https" {
return NULL
}

// do HTTP request
req, err := http.NewRequest("GET", u.String(), http.NoBody)
if err != nil {
return ErrorString(err)
}
req.Header.Set("User-Agent", defaults.UserAgent)
i.logger.Debug().Str("fn", "fetch").Str("url", rawurl).Msg("do HTTP request...")
res, err := defaults.HTTPClient.Do(req)
if err != nil {
return ErrorString(err)
}
defer res.Body.Close()

// validate HTTP response
if res.StatusCode != 200 {
return ErrorString(fmt.Errorf("invalid status code: %s", res.Status))
}
contentType := res.Header.Get("Content-type")
if !strings.HasPrefix(contentType, "text/html") {
return ErrorString(fmt.Errorf("invalid content-type: %s", contentType))
}

// read body response
body, err := charset.NewReader(res.Body, contentType)
if err != nil {
return ErrorString(err)
}
body = io.LimitReader(body, MAX_RESPONSE_SIZE)
buf := new(strings.Builder)
if _, err := io.Copy(buf, body); err != nil {
return ErrorString(err)
}

return &object.String{
Value: buf.String(),
}
}
3 changes: 2 additions & 1 deletion internal/scripting/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ func (i *Interpreter) init() {
// alter builtins functions
i.eval.AddFunction("print", i.fnPrint)
i.eval.AddFunction("printf", i.fnPrintf)
i.eval.AddFunction("fetch", i.fnFetch)
// add custom functions
i.eval.AddFunction("triggerWebhook", i.buildSingleArgFunction(OpTriggerWebhook))
i.eval.AddFunction("sendNotification", i.buildNoArgFunction(OpSendNotification))
i.eval.AddFunction("setCategory", i.buildSingleArgFunction(OpSetCategory))
i.eval.AddFunction("setTitle", i.buildSingleArgFunction(OpSetTitle))
i.eval.AddFunction("setText", i.buildSingleArgFunction(OpSetTitle))
i.eval.AddFunction("setHTML", i.buildSingleArgFunction(OpSetHTML))
i.eval.AddFunction("markAsRead", i.buildNoArgFunction(OpMarkAsRead))
i.eval.AddFunction("markAsToRead", i.buildNoArgFunction(OpMarkAsToRead))
i.eval.AddFunction("disableGlobalNotification", i.buildNoArgFunction(OpDisableGlobalNotification))
}

Expand Down
4 changes: 4 additions & 0 deletions internal/scripting/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const (
OpSetTitle
// OpSetCategory to set article category
OpSetCategory
// MarkAsRead to set articte status as "read"
OpMarkAsRead
// MarkAsToRead to set articte status as "to_read"
OpMarkAsToRead
// OpDisableGlobalNotification to disable global notification
OpDisableGlobalNotification
)
Expand Down
15 changes: 15 additions & 0 deletions internal/service/scripting.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func (reg *Registry) execSetOperations(ctx context.Context, ops scripting.Operat
func (reg *Registry) execOtherOperations(ctx context.Context, ops scripting.OperationStack, article *model.Article) error {
// allows only 2 webhook trigger
hardLimitCounter := 2
// status to update if asked
status := ""
for _, op := range ops {
switch op.Name {
case scripting.OpSendNotification:
Expand All @@ -106,6 +108,19 @@ func (reg *Registry) execOtherOperations(ctx context.Context, ops scripting.Oper
if _, err := reg.SendArticle(ctx, article.ID, &name); err != nil {
return err
}
case scripting.OpMarkAsRead:
status = "read"
case scripting.OpMarkAsToRead:
status = "to_read"
}
}
if status != "" {
update := model.ArticleUpdateForm{
ID: article.ID,
Status: &status,
}
if _, err := reg.UpdateArticle(ctx, update); err != nil {
return err
}
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/highlight/evalfiter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function(hljs) {
$pattern: hljs.UNDERSCORE_IDENT_RE,
literal: 'true false nil',
keyword: 'case default else for foreach function if in local return switch while',
built_in: 'between day float hour int keys len lower match max min minute month now panic printf print reverse seconds sort split sprintf string time trim type upper weekday year setText setTitle setHTML setCategory fetch sendNotification triggerWebhook disableGlobalNotification',
built_in: 'between day float hour int keys len lower match max min minute month now panic printf print reverse seconds sort split sprintf string time trim type upper weekday year setText setTitle setHTML setCategory markAsRead markAsToRead sendNotification triggerWebhook disableGlobalNotification',
},
contains: [
hljs.C_NUMBER_MODE,
Expand Down

0 comments on commit d444a4f

Please sign in to comment.