Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F77427
preums.ex
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
preums.ex
View Options
defmodule
Nola.Plugins.Preums
do
@moduledoc
"""
#
preums !!!
* `!preums`: affiche le preums du jour
* `.preums`: stats des preums
"""
# WIP Scores
# L'idée c'est de donner un score pour mettre un peu de challenge en pénalisant les preums faciles.
#
# Un preums ne vaut pas 1 point, mais plutôt 0.10 ou 0.05, et on arrondi au plus proche. C'est un jeu sur le long
# terme. Un gros bonus pourrait apporter beaucoup de points.
#
# Il faudrait ces données:
# - moyenne des preums
# - activité récente du channel et par nb actifs d'utilisateurs
# (aggréger memberships+usertrack last_active ?)
# (faire des stats d'activité habituelle (un peu a la pisg) ?)
# - preums consécutifs
#
# Malus:
# - est proche de la moyenne en faible activité
# - trop consécutif de l'utilisateur sauf si activité
#
# Bonus:
# - plus le preums est éloigné de la moyenne
# - après 18h double
# - plus l'activité est élévée, exponentiel selon la moyenne
# - derns entre 4 et 6 (pourrait être adapté selon les stats d'activité)
#
# WIP Badges:
# - derns
# - streaks
# - faciles
# - ?
require
Logger
@perfects
[
~r/preum(s|)/i
]
# dets {{chan, day = {yyyy, mm, dd}}, nick, now, perfect?, text}
def
all
(
dets
)
do
:dets
.
foldl
(
fn
(
i
,
acc
)
->
[
i
|
acc
]
end
,
[],
dets
)
end
def
all
(
dets
,
channel
)
do
fun
=
fn
({{
chan
,
date
},
account_id
,
time
,
perfect
,
text
},
acc
)
->
if
channel
==
chan
do
[%{
date
:
date
,
account_id
:
account_id
,
time
:
time
,
perfect
:
perfect
,
text
:
text
}
|
acc
]
else
acc
end
end
:dets
.
foldl
(
fun
,
[],
dets
)
end
def
topnicks
(
dets
,
channel
,
options
\\
[])
do
sort_elem
=
case
Keyword
.
get
(
options
,
:sort_by
,
:score
)
do
:score
->
1
:count
->
0
end
fun
=
fn
(
x
=
{{
chan
,
date
},
account_id
,
time
,
perfect
,
text
},
acc
)
->
if
(
channel
==
nil
and
chan
)
or
(
channel
==
chan
)
do
{
count
,
points
}
=
Map
.
get
(
acc
,
account_id
,
{
0
,
0
})
score
=
score
(
chan
,
account_id
,
time
,
perfect
,
text
)
Map
.
put
(
acc
,
account_id
,
{
count
+
1
,
points
+
score
})
else
acc
end
end
:dets
.
foldl
(
fun
,
%{},
dets
)
|>
Enum
.
sort_by
(
fn
({
_account_id
,
value
})
->
elem
(
value
,
sort_elem
)
end
,
&>=/
2
)
end
def
irc_doc
,
do
:
@moduledoc
def
start_link
()
do
GenServer
.
start_link
(
__MODULE__
,
[],
name
:
__MODULE__
)
end
def
dets
do
(
Nola
.
data_path
()
<>
"/preums.dets"
)
|>
String
.
to_charlist
()
end
def
init
([])
do
regopts
=
[
plugin
:
__MODULE__
]
{
:ok
,
_
}
=
Registry
.
register
(
Nola.PubSub
,
"account"
,
regopts
)
{
:ok
,
_
}
=
Registry
.
register
(
Nola.PubSub
,
"messages"
,
regopts
)
{
:ok
,
_
}
=
Registry
.
register
(
Nola.PubSub
,
"triggers"
,
regopts
)
{
:ok
,
dets
}
=
:dets
.
open_file
(
dets
(),
[{
:repair
,
:force
}])
Util
.
ets_mutate_select_each
(
:dets
,
dets
,
[{
:"$1"
,
[],
[
:"$1"
]}],
fn
(
table
,
obj
)
->
{
key
,
nick
,
now
,
perfect
,
text
}
=
obj
case
key
do
{{
net
,
{
bork
,
chan
}},
date
}
->
:dets
.
delete
(
table
,
key
)
nick
=
if
Nola.Account
.
get
(
nick
)
do
nick
else
if
acct
=
Nola.Account
.
find_always_by_nick
(
net
,
nil
,
nick
)
do
acct
.
id
else
nick
end
end
:dets
.
insert
(
table
,
{
{
{
net
,
chan
},
date
},
nick
,
now
,
perfect
,
text
})
{{
_net
,
nil
},
_
}
->
:dets
.
delete
(
table
,
key
)
{{
net
,
chan
},
date
}
->
if
!
Nola.Account
.
get
(
nick
)
do
if
acct
=
Nola.Account
.
find_always_by_nick
(
net
,
chan
,
nick
)
do
:dets
.
delete
(
table
,
key
)
:dets
.
insert
(
table
,
{
{
{
net
,
chan
},
date
},
acct
.
id
,
now
,
perfect
,
text
})
end
end
_
->
Logger
.
debug
(
"DID NOT FIX:
#{
inspect
key
}
"
)
end
end
)
{
:ok
,
%{
dets
:
dets
}}
end
# Latest
def
handle_info
({
:irc
,
:trigger
,
"preums"
,
m
=
%
Nola.Message
{
trigger
:
%
Nola.Trigger
{
type
:
:bang
}}},
state
)
do
channelkey
=
{
m
.
network
,
m
.
channel
}
state
=
handle_preums
(
m
,
state
)
tz
=
timezone
(
channelkey
)
{
:ok
,
now
}
=
DateTime
.
now
(
tz
,
Tzdata.TimeZoneDatabase
)
date
=
{
now
.
year
,
now
.
month
,
now
.
day
}
key
=
{
channelkey
,
date
}
chan_cache
=
Map
.
get
(
state
,
channelkey
,
%{})
item
=
if
i
=
Map
.
get
(
chan_cache
,
date
)
do
i
else
case
:dets
.
lookup
(
state
.
dets
,
key
)
do
[
item
=
{
^
key
,
_account_id
,
_now
,
_perfect
,
_text
}]
->
item
_
->
nil
end
end
if
item
do
{
_
,
account_id
,
date
,
_perfect
,
text
}
=
item
h
=
"
#{
date
.
hour
}
:
#{
date
.
minute
}
:
#{
date
.
second
}
"
account
=
Nola.Account
.
get
(
account_id
)
user
=
Nola.UserTrack
.
find_by_account
(
m
.
network
,
account
)
nick
=
if
(
user
,
do
:
user
.
nick
,
else
:
account
.
name
)
m
.
replyfun
.
(
"preums:
#{
nick
}
à
#{
h
}
: “
#{
text
}
”"
)
end
{
:noreply
,
state
}
end
# Stats
def
handle_info
({
:irc
,
:trigger
,
"preums"
,
m
=
%
Nola.Message
{
trigger
:
%
Nola.Trigger
{
type
:
:dot
}}},
state
)
do
channel
=
{
m
.
network
,
m
.
channel
}
state
=
handle_preums
(
m
,
state
)
top
=
topnicks
(
state
.
dets
,
channel
,
sort_by
:
:score
)
|>
Enum
.
map
(
fn
({
account_id
,
{
count
,
score
}})
->
account
=
Nola.Account
.
get
(
account_id
)
user
=
Nola.UserTrack
.
find_by_account
(
m
.
network
,
account
)
nick
=
if
(
user
,
do
:
user
.
nick
,
else
:
account
.
name
)
"
#{
nick
}
:
#{
score
}
(
#{
count
}
)"
end
)
|>
Enum
.
intersperse
(
", "
)
|>
Enum
.
join
(
""
)
msg
=
unless
top
==
""
do
"top preums:
#{
top
}
"
else
"vous êtes tous nuls"
end
m
.
replyfun
.
(
msg
)
{
:noreply
,
state
}
end
# Help
def
handle_info
({
:irc
,
:trigger
,
"preums"
,
m
=
%
Nola.Message
{
trigger
:
%
Nola.Trigger
{
type
:
:query
}}},
state
)
do
state
=
handle_preums
(
m
,
state
)
msg
=
"!preums - preums du jour, .preums top preumseurs"
m
.
replymsg
.
(
msg
)
{
:noreply
,
state
}
end
# Trigger fallback
def
handle_info
({
:irc
,
:trigger
,
_
,
m
=
%
Nola.Message
{}},
state
)
do
state
=
handle_preums
(
m
,
state
)
{
:noreply
,
state
}
end
# Message fallback
def
handle_info
({
:irc
,
:text
,
m
=
%
Nola.Message
{}},
state
)
do
{
:noreply
,
handle_preums
(
m
,
state
)}
end
# Account
def
handle_info
({
:account_change
,
old_id
,
new_id
},
state
)
do
spec
=
[{{
:_
,
:"$1"
,
:_
,
:_
,
:_
},
[{
:==
,
:"$1"
,
{
:const
,
old_id
}}],
[
:"$_"
]}]
Util
.
ets_mutate_select_each
(
:dets
,
state
.
dets
,
spec
,
fn
(
table
,
obj
)
->
rename_object_owner
(
table
,
obj
,
new_id
)
end
)
{
:noreply
,
state
}
end
# Account: move from nick to account id
# FIXME: Doesn't seem to work.
def
handle_info
({
:accounts
,
accounts
},
state
)
do
for
x
=
{
:account
,
_net
,
_chan
,
_nick
,
_account_id
}
<-
accounts
do
handle_info
(
x
,
state
)
end
{
:noreply
,
state
}
end
def
handle_info
({
:account
,
_net
,
_chan
,
nick
,
account_id
},
state
)
do
nick
=
String
.
downcase
(
nick
)
spec
=
[{{
:_
,
:"$1"
,
:_
,
:_
,
:_
},
[{
:==
,
:"$1"
,
{
:const
,
nick
}}],
[
:"$_"
]}]
Util
.
ets_mutate_select_each
(
:dets
,
state
.
dets
,
spec
,
fn
(
table
,
obj
)
->
Logger
.
debug
(
"account:: merging
#{
nick
}
->
#{
account_id
}
"
)
rename_object_owner
(
table
,
obj
,
account_id
)
end
)
{
:noreply
,
state
}
end
def
handle_info
(
_
,
dets
)
do
{
:noreply
,
dets
}
end
defp
rename_object_owner
(
table
,
object
=
{
key
,
_
,
now
,
perfect
,
time
},
new_id
)
do
:dets
.
delete_object
(
table
,
key
)
:dets
.
insert
(
table
,
{
key
,
new_id
,
now
,
perfect
,
time
})
end
defp
timezone
(
channel
)
do
env
=
Application
.
get_env
(
:nola
,
Nola.Plugins.Preums
,
[])
channels
=
Keyword
.
get
(
env
,
:channels
,
%{})
channel_settings
=
Map
.
get
(
channels
,
channel
,
[])
default
=
Keyword
.
get
(
env
,
:default_tz
,
"Europe/Paris"
)
Keyword
.
get
(
channel_settings
,
:tz
,
default
)
||
default
end
defp
handle_preums
(%
Nola.Message
{
channel
:
nil
},
state
)
do
state
end
defp
handle_preums
(
m
=
%
Nola.Message
{
text
:
text
,
sender
:
sender
},
state
)
do
channel
=
{
m
.
network
,
m
.
channel
}
tz
=
timezone
(
channel
)
{
:ok
,
now
}
=
DateTime
.
now
(
tz
,
Tzdata.TimeZoneDatabase
)
date
=
{
now
.
year
,
now
.
month
,
now
.
day
}
key
=
{
channel
,
date
}
chan_cache
=
Map
.
get
(
state
,
channel
,
%{})
unless
i
=
Map
.
get
(
chan_cache
,
date
)
do
case
:dets
.
lookup
(
state
.
dets
,
key
)
do
[
item
=
{
^
key
,
_nick
,
_now
,
_perfect
,
_text
}]
->
# Preums lost, but wasn't cached
Map
.
put
(
state
,
channel
,
%{
date
=>
item
})
[]
->
# Preums won!
perfect?
=
Enum
.
any?
(
@perfects
,
fn
(
perfect
)
->
Regex
.
match?
(
perfect
,
text
)
end
)
item
=
{
key
,
m
.
account
.
id
,
now
,
perfect?
,
text
}
:dets
.
insert
(
state
.
dets
,
item
)
:dets
.
sync
(
state
.
dets
)
Map
.
put
(
state
,
channel
,
%{
date
=>
item
})
{
:error
,
_
}
=
error
->
Logger
.
error
(
"
#{
__MODULE__
}
dets lookup failed:
#{
inspect
error
}
"
)
state
end
else
state
end
end
def
score
(
_chan
,
_account
,
_time
,
_perfect
,
_text
)
do
1
end
end
File Metadata
Details
Attached
Mime Type
text/x-ruby
Expires
Mon, Jul 7, 3:07 PM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
49908
Default Alt Text
preums.ex (8 KB)
Attached To
rNOLA Nola
Event Timeline
Log In to Comment