-
Notifications
You must be signed in to change notification settings - Fork 940
/
Copy pathpatreonpremiumsource.go
181 lines (147 loc) · 4.45 KB
/
patreonpremiumsource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package patreonpremiumsource
import (
"context"
"fmt"
"time"
"emperror.dev/errors"
"github.com/jonas747/yagpdb/common"
"github.com/jonas747/yagpdb/common/patreon"
"github.com/jonas747/yagpdb/premium"
"github.com/jonas747/yagpdb/premium/models"
"github.com/jonas747/yagpdb/web"
"github.com/volatiletech/sqlboiler/queries/qm"
)
type PremiumSource struct{}
func RegisterPlugin() {
logger.Info("Registered patreon premium source")
premium.RegisterPremiumSource(&PremiumSource{})
common.RegisterPlugin(&Plugin{})
}
func (ps *PremiumSource) Init() {}
func (ps *PremiumSource) Names() (human string, idname string) {
return "Patreon", "patreon"
}
var logger = common.GetPluginLogger(&Plugin{})
// Since we keep the patrons loaded on the webserver, we also do the updating of the patreon premium slots on the webserver
// in the future this should be cleaned up (maybe keep patrons in redis)
type Plugin struct{}
func (p *Plugin) PluginInfo() *common.PluginInfo {
return &common.PluginInfo{
Name: "Patreon premium source",
SysName: "patreon_premium_source",
Category: common.PluginCategoryCore,
}
}
var _ web.Plugin = (*Plugin)(nil)
func (p *Plugin) InitWeb() {
if patreon.ActivePoller == nil {
return
}
go RunPoller()
}
func RunPoller() {
ticker := time.NewTicker(time.Minute)
for {
<-ticker.C
err := UpdatePremiumSlots(context.Background())
if err != nil {
logger.WithError(err).Error("Failed updating premium slots for patrons")
}
}
}
func UpdatePremiumSlots(ctx context.Context) error {
tx, err := common.PQ.BeginTx(ctx, nil)
if err != nil {
return errors.WithMessage(err, "BeginTX")
}
slots, err := models.PremiumSlots(qm.Where("source='patreon'"), qm.OrderBy("id desc")).All(ctx, tx)
if err != nil {
tx.Rollback()
return errors.WithMessage(err, "PremiumSlots")
}
patrons := patreon.ActivePoller.GetPatrons()
// Sort the slots into a map of users -> slots
sorted := make(map[int64][]*models.PremiumSlot)
for _, slot := range slots {
sorted[slot.UserID] = append(sorted[slot.UserID], slot)
}
// Update already tracked slots
for userID, userSlots := range sorted {
slotsForPledge := 0
for _, patron := range patrons {
if patron.DiscordID == userID {
slotsForPledge = CalcSlotsForPledge(patron.AmountCents)
break
}
}
if slotsForPledge == len(userSlots) {
// Correct number of slots already set up
continue
}
if slotsForPledge > len(userSlots) {
// Need to create more slots
for i := 0; i < slotsForPledge-len(userSlots); i++ {
title := fmt.Sprintf("Patreon Slot #%d", i+len(userSlots))
slot, err := premium.CreatePremiumSlot(ctx, tx, userID, "patreon", title, "Slot is available for as long as the pledge is active on patreon", int64(i+len(userSlots)), -1)
if err != nil {
tx.Rollback()
return errors.WithMessage(err, "CreatePremiumSlot")
}
logger.Info("Created patreon premium slot #", slot.ID, slot.UserID)
}
} else if slotsForPledge < len(userSlots) {
// Need to remove slots
slotsToRemove := make([]int64, 0)
for i := 0; i < len(userSlots)-slotsForPledge; i++ {
slot := userSlots[i]
slotsToRemove = append(slotsToRemove, slot.ID)
logger.Info("Marked patreon slot for deletion #", slot.ID, slot.UserID)
}
err = premium.RemovePremiumSlots(ctx, tx, userID, slotsToRemove)
if err != nil {
tx.Rollback()
return errors.WithMessage(err, "RemovePremiumSlots")
}
}
}
// Add completely new premium slots
OUTER:
for _, v := range patrons {
if v.DiscordID == 0 {
continue
}
for userID, _ := range sorted {
if userID == v.DiscordID {
continue OUTER
}
}
// If we got here then that means this is a new user
slots := CalcSlotsForPledge(v.AmountCents)
for i := 0; i < slots; i++ {
title := fmt.Sprintf("Patreon Slot #%d", i+1)
slot, err := premium.CreatePremiumSlot(ctx, tx, v.DiscordID, "patreon", title, "Slot is available for as long as the pledge is active on patreon", int64(i+1), -1)
if err != nil {
tx.Rollback()
return errors.WithMessage(err, "new CreatePremiumSlot")
}
logger.Info("Created new patreon premium slot #", slot.ID, slot.ID)
}
}
err = tx.Commit()
return errors.WithMessage(err, "Commit")
}
func CalcSlotsForPledge(cents int) (slots int) {
if cents < 300 {
return 0
}
// 3$ for one slot
if cents >= 300 && cents < 500 {
return 1
}
// 2.5$ per slot up until before 10$
if cents < 1000 {
return cents / 250
}
// 2$ per slot from and including 10$
return cents / 200
}