Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

events this week functionality #12

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 51 additions & 20 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import (
)

type EngineConfig struct {
Channels []channels.Channel
Sources []sources.Source
Rules []rules.NotifyRule
RunAt RunAt
Location *time.Location
DebugMode bool
NowFunc func() time.Time
Channels []channels.Channel
Sources []sources.Source
Rules []rules.NotifyRule
WeeklySummaryTemplates map[string]rules.WeeklySummaryFunc
WeeklySummaryDay time.Weekday
RunAt RunAt
Location *time.Location
DebugMode bool
}

type RunAt struct {
Expand Down Expand Up @@ -63,9 +66,16 @@ func (e *Engine) RunOnce() {
continue
}

eventsInNextWeek := []sources.Event{}
dayOfWeek := e.config.NowFunc().Weekday()

for _, event := range events {
if eventIsWithinNextWeek(event, e.config.NowFunc()) {
eventsInNextWeek = append(eventsInNextWeek, event)
}

for _, rule := range e.config.Rules {
if !rule.EventIsApplicable(event, e.config.Location) {
if !rule.EventIsApplicable(event, e.config.Location, e.config.NowFunc()) {
continue
}

Expand All @@ -79,22 +89,43 @@ func (e *Engine) RunOnce() {
if !ok {
log.Println(fmt.Errorf("did not find message for %s channel %s", channel.Type(), channel.Name()))
}
e.sendMessage(channel, msg)
}
}
}

log.Printf("sending event to %s on %s: %s\n", channel.Name(), channel.Type(), event.Name)
if e.config.DebugMode {
fmt.Printf("%s\n\n", msg)
} else {
if err := channel.Send(msg); err != nil {
log.Println(fmt.Errorf(
"failed to send to %s channel %s: %w",
channel.Type(),
channel.Name(),
err,
))
}
}
if dayOfWeek == e.config.WeeklySummaryDay {
for _, channel := range e.config.Channels {
msgFunc, ok := e.config.WeeklySummaryTemplates[channel.Type()]
if !ok {
log.Println(fmt.Errorf("did not find message for %s channel %s", channel.Type(), channel.Name()))
} else {
e.sendMessage(channel, msgFunc(eventsInNextWeek))
}
}
}
}
}

func (e *Engine) sendMessage(channel channels.Channel, message string) {
log.Printf("sending weekly summary to %s on %s\n", channel.Name(), channel.Type())

if e.config.DebugMode {
fmt.Printf("%s\n\n", message)
} else {
if err := channel.Send(message); err != nil {
log.Println(fmt.Errorf(
"failed to send to %s channel %s: %w",
channel.Type(),
channel.Name(),
err,
))
}
}
}

func eventIsWithinNextWeek(event sources.Event, now time.Time) bool {
diff := now.Sub(event.DateTime)
inNextWeek := diff < 7*24*time.Hour
return inNextWeek
}
34 changes: 30 additions & 4 deletions engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package engine_test

import (
"fmt"
"strings"
"testing"
"time"

Expand All @@ -11,6 +12,8 @@ import (
"github.com/devict/promobot/sources"
)

var octoberFirst = time.Date(2012, time.October, 1, 8, 0, 0, 0, time.Local)

type TestChannel struct {
name string
sentMsgs []string
Expand All @@ -36,8 +39,10 @@ func NewTestSource(name string, events []sources.Event) *TestSource {
return &TestSource{name: name, events: events}
}

func (c *TestSource) Name() string { return c.name }
func (c *TestSource) Type() string { return "test" }
func (c *TestSource) Name() string { return c.name }
func (c *TestSource) Type() string { return "test" }
func (c *TestSource) JsonUrl() string { return "nope" }
func (c *TestSource) HtmlUrl() string { return "https://meetup.com/devict" }
func (c *TestSource) Retrieve(t *time.Location) ([]sources.Event, error) {
return c.events, nil
}
Expand All @@ -48,7 +53,7 @@ func testEvent(name, source string, daysAhead int) sources.Event {
Source: source,
URL: "https://devict.org",
Location: "Definitely not the metaverse",
DateTime: time.Now().Add(time.Duration(daysAhead*24) * time.Hour),
DateTime: octoberFirst.Add(time.Duration(daysAhead*24) * time.Hour),
}
}

Expand All @@ -57,6 +62,7 @@ func TestEngine(t *testing.T) {
testChannel2 := NewTestChannel("test2")

config := engine.EngineConfig{
NowFunc: func() time.Time { return octoberFirst },
Channels: []channels.Channel{
channels.Channel(testChannel1),
channels.Channel(testChannel2),
Expand All @@ -73,6 +79,17 @@ func TestEngine(t *testing.T) {
testEvent("Test Event 8", "test-source-1", 8),
})),
},
WeeklySummaryDay: octoberFirst.Weekday(),
WeeklySummaryTemplates: map[string]rules.WeeklySummaryFunc{
"test": func(events []sources.Event) string {
eventLines := []string{}
for _, event := range events {
day := event.DateTime.Weekday().String()
eventLines = append(eventLines, fmt.Sprintf("- [%s] <%s|%s>", day[:3], event.URL, event.Name))
}
return fmt.Sprintf("Events this week!\n\n%s", strings.Join(eventLines, "\n"))
},
},
Rules: []rules.NotifyRule{
{
NumDaysOut: 1,
Expand Down Expand Up @@ -108,6 +125,15 @@ func TestEngine(t *testing.T) {
"1 day until Test Event 1 from test-source-1",
"3 days until Test Event 3 from test-source-1",
"7 days until Test Event 7 from test-source-1",
strings.Join([]string{
"Events this week!\n",
"- [Tue] <https://devict.org|Test Event 1>",
"- [Wed] <https://devict.org|Test Event 2>",
"- [Thu] <https://devict.org|Test Event 3>",
"- [Fri] <https://devict.org|Test Event 4>",
"- [Sat] <https://devict.org|Test Event 5>",
"- [Sun] <https://devict.org|Test Event 6>",
}, "\n"),
}

if len(testChannel1.sentMsgs) != len(expected) {
Expand Down Expand Up @@ -138,7 +164,7 @@ func containsStr(slice []string, str string) bool {
}

func TestShouldRun(t *testing.T) {
now := time.Now()
now := octoberFirst

e := engine.NewEngine(engine.EngineConfig{
RunAt: engine.RunAt{
Expand Down
28 changes: 22 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"strings"
"time"

"github.com/devict/promobot/channels"
Expand All @@ -19,9 +20,9 @@ type config struct {
DevICTTwitter DevICTTwitterConfig

// Sources
DevICTMeetupURL string `envconfig:"DEVICT_MEETUP_URL" required:"true"`
OzSecMeetupURL string `envconfig:"OZSEC_MEETUP_URL" required:"true"`
WTFMeetupURL string `envconfig:"WTF_MEETUP_URL" required:"true"`
DevICTMeetupURLName string `envconfig:"DEVICT_MEETUP_URL_NAME" required:"true"`
OzSecMeetupURLName string `envconfig:"OZSEC_MEETUP_URL_NAME" required:"true"`
WTFMeetupURLName string `envconfig:"WTF_MEETUP_URL_NAME" required:"true"`
}

type DevICTTwitterConfig struct {
Expand All @@ -40,10 +41,11 @@ func main() {
loc, _ := time.LoadLocation("America/Chicago")

e := engine.NewEngine(engine.EngineConfig{
NowFunc: func() time.Time { return time.Now() },
Sources: []sources.Source{
sources.Source(sources.NewMeetupSource("devICT", c.DevICTMeetupURL)),
sources.Source(sources.NewMeetupSource("OzSec", c.OzSecMeetupURL)),
sources.Source(sources.NewMeetupSource("Wichita Technology Forum", c.WTFMeetupURL)),
sources.Source(sources.NewMeetupSource("devICT", c.DevICTMeetupURLName)),
sources.Source(sources.NewMeetupSource("OzSec", c.OzSecMeetupURLName)),
sources.Source(sources.NewMeetupSource("Wichita Technology Forum", c.WTFMeetupURLName)),
},
Channels: []channels.Channel{
channels.Channel(channels.NewSlackChannel("devICT", c.DevICTSlackWebhook)),
Expand All @@ -54,6 +56,20 @@ func main() {
APISecretKey: c.DevICTTwitter.APISecretKey,
})),
},
WeeklySummaryTemplates: map[string]rules.WeeklySummaryFunc{
"slack": func(events []sources.Event) string {
eventLines := []string{}
for _, event := range events {
day := event.DateTime.Weekday().String()
eventLines = append(eventLines, fmt.Sprintf("- [%s] <%s|%s>", day[:3], event.URL, event.Name))
}
return fmt.Sprintf("Events this week!\n\n%s", strings.Join(eventLines, "\n"))
},
// "twitter": func(events []sources.Event) string {
// return ""
// },
},
WeeklySummaryDay: time.Tuesday,
Rules: []rules.NotifyRule{
{
NumDaysOut: 10,
Expand Down
8 changes: 5 additions & 3 deletions rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

type MsgFunc func(sources.Event) string

type WeeklySummaryFunc func([]sources.Event) string

type NotifyRule struct {
NumDaysOut int
ChannelTemplates map[string]MsgFunc
Expand All @@ -21,13 +23,13 @@ func (rule NotifyRule) MessagesFromEvent(event sources.Event) (map[string]string
return channelMessages, nil
}

func (rule NotifyRule) EventIsApplicable(event sources.Event, loc *time.Location) bool {
func (rule NotifyRule) EventIsApplicable(event sources.Event, loc *time.Location, now time.Time) bool {
// don't promote the event if it's already started, in case of 0 days out rule
if time.Now().In(loc).After(event.DateTime) {
if now.In(loc).After(event.DateTime) {
return false
}

checkDate := dateFromTime(time.Now().Add(time.Duration(rule.NumDaysOut*24) * time.Hour))
checkDate := dateFromTime(now.Add(time.Duration(rule.NumDaysOut*24) * time.Hour))
eventDate := dateFromTime(event.DateTime)
return eventDate.Equal(checkDate)
}
Expand Down
16 changes: 12 additions & 4 deletions sources/meetup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,36 @@ package sources

import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

type MeetupSource struct {
name string
url string
urlName string

}

func NewMeetupSource(name, url string) *MeetupSource {
func NewMeetupSource(name, urlName string) *MeetupSource {
return &MeetupSource{
name: name,
url: url,
urlName: urlName,
}
}

func (c *MeetupSource) Name() string { return c.name }
func (c *MeetupSource) Type() string { return "meetup" }
func (c *MeetupSource) JsonUrl() string {
return fmt.Sprintf("https://api.meetup.com/2/events?&sign=true&photo-host=public&group_urlname=%s&limited_events=false&fields=series&status=upcoming&page=20", c.urlName)
}
func (c *MeetupSource) HtmlUrl() string {
return fmt.Sprintf("https://meetup.com/%s", c.urlName)
}

func (c *MeetupSource) Retrieve(loc *time.Location) ([]Event, error) {
resp, err := http.Get(c.url)
resp, err := http.Get(c.JsonUrl())
if err != nil {
return []Event{}, err
}
Expand Down
2 changes: 2 additions & 0 deletions sources/sources.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import "time"
type Source interface {
Name() string
Type() string
JsonUrl() string
HtmlUrl() string
Retrieve(*time.Location) ([]Event, error)
}

Expand Down