mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-04-04 12:27:36 +03:00
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
package script
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/common/logger"
|
|
|
|
"github.com/adhocore/gronx"
|
|
)
|
|
|
|
var _ adapter.GenericScript = (*SurgeCronScript)(nil)
|
|
|
|
type SurgeCronScript struct {
|
|
GenericScript
|
|
ctx context.Context
|
|
expression string
|
|
timer *time.Timer
|
|
}
|
|
|
|
func NewSurgeCronScript(ctx context.Context, logger logger.ContextLogger, options option.Script) (*SurgeCronScript, error) {
|
|
source, err := NewSource(ctx, logger, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !gronx.IsValid(options.CronOptions.Expression) {
|
|
return nil, E.New("invalid cron expression: ", options.CronOptions.Expression)
|
|
}
|
|
return &SurgeCronScript{
|
|
GenericScript: GenericScript{
|
|
logger: logger,
|
|
tag: options.Tag,
|
|
timeout: time.Duration(options.Timeout),
|
|
arguments: options.Arguments,
|
|
source: source,
|
|
},
|
|
ctx: ctx,
|
|
expression: options.CronOptions.Expression,
|
|
}, nil
|
|
}
|
|
|
|
func (s *SurgeCronScript) Type() string {
|
|
return C.ScriptTypeSurgeCron
|
|
}
|
|
|
|
func (s *SurgeCronScript) Tag() string {
|
|
return s.tag
|
|
}
|
|
|
|
func (s *SurgeCronScript) StartContext(ctx context.Context, startContext *adapter.HTTPStartContext) error {
|
|
return s.source.StartContext(ctx, startContext)
|
|
}
|
|
|
|
func (s *SurgeCronScript) PostStart() error {
|
|
err := s.source.PostStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go s.loop()
|
|
return nil
|
|
}
|
|
|
|
func (s *SurgeCronScript) loop() {
|
|
s.logger.Debug("starting event")
|
|
err := s.Run(s.ctx)
|
|
if err != nil {
|
|
s.logger.Error(E.Cause(err, "running event"))
|
|
}
|
|
nextTick, err := gronx.NextTick(s.expression, false)
|
|
if err != nil {
|
|
s.logger.Error(E.Cause(err, "determine next tick"))
|
|
return
|
|
}
|
|
s.timer = time.NewTimer(nextTick.Sub(time.Now()))
|
|
s.logger.Debug("next event at: ", nextTick.Format(log.DefaultTimeFormat))
|
|
for {
|
|
select {
|
|
case <-s.ctx.Done():
|
|
return
|
|
case <-s.timer.C:
|
|
s.logger.Debug("starting event")
|
|
err = s.Run(s.ctx)
|
|
if err != nil {
|
|
s.logger.Error(E.Cause(err, "running event"))
|
|
}
|
|
nextTick, err = gronx.NextTick(s.expression, false)
|
|
if err != nil {
|
|
s.logger.Error(E.Cause(err, "determine next tick"))
|
|
return
|
|
}
|
|
s.timer.Reset(nextTick.Sub(time.Now()))
|
|
s.logger.Debug("next event at: ", nextTick)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SurgeCronScript) Close() error {
|
|
return s.source.Close()
|
|
}
|
|
|
|
func (s *SurgeCronScript) Run(ctx context.Context) error {
|
|
program := s.source.Program()
|
|
if program == nil {
|
|
return E.New("invalid script")
|
|
}
|
|
ctx, cancel := context.WithCancelCause(ctx)
|
|
defer cancel(nil)
|
|
vm := NewRuntime(ctx, s.logger, cancel)
|
|
err := SetSurgeModules(vm, ctx, s.logger, cancel, s.Tag(), s.Type(), s.arguments)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return ExecuteSurgeGeneral(vm, program, ctx, s.timeout)
|
|
}
|