Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F665943
account.ex
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Subscribers
None
account.ex
View Options
defmodule
Nola.Account
do
alias
IRC.UserTrack.User
@moduledoc
"""
Account registry....
Maps a network predicate:
* `{net, {:nick, nickname}}`
* `{net, {:account, account}}`
* `{net, {:mask, user@host}}`
to an unique identifier, that can be shared over multiple networks.
If a predicate cannot be found for an existing account, a new account will be made in the database.
To link two existing accounts from different network onto a different one, a merge operation is provided.
"""
# FIXME: Ensure uniqueness of name?
@derive
{
Poison.Encoder
,
except
:
[
:token
]}
defstruct
[
:id
,
:name
,
:token
]
@type
t
::
%
__MODULE__
{
id
:
id
(),
name
:
String
.
t
()}
@type
id
::
String
.
t
()
defimpl
Inspect
,
for
:
__MODULE__
do
import
Inspect.Algebra
def
inspect
(%{
id
:
id
,
name
:
name
},
opts
)
do
concat
([
"
#
Nola.Account["
,
id
,
" "
,
name
,
"]"
])
end
end
def
file
(
base
)
do
to_charlist
(
Nola
.
data_path
()
<>
"/account_
#{
base
}
.dets"
)
end
defp
from_struct
(
%
__MODULE__
{
id
:
id
,
name
:
name
,
token
:
token
})
do
{
id
,
name
,
token
}
end
defp
from_tuple
({
id
,
name
,
token
})
do
%
__MODULE__
{
id
:
id
,
name
:
name
,
token
:
token
}
end
def
start_link
()
do
GenServer
.
start_link
(
__MODULE__
,
[],
[
name
:
__MODULE__
])
end
def
init
(
_
)
do
{
:ok
,
accounts
}
=
:dets
.
open_file
(
file
(
"db"
),
[])
{
:ok
,
meta
}
=
:dets
.
open_file
(
file
(
"meta"
),
[])
{
:ok
,
predicates
}
=
:dets
.
open_file
(
file
(
"predicates"
),
[{
:type
,
:set
}])
{
:ok
,
%{
accounts
:
accounts
,
meta
:
meta
,
predicates
:
predicates
}}
end
def
get
(
id
)
do
case
:dets
.
lookup
(
file
(
"db"
),
id
)
do
[
account
]
->
from_tuple
(
account
)
_
->
nil
end
end
def
get_by_name
(
name
)
do
spec
=
[{{
:_
,
:"$1"
,
:_
},
[{
:==
,
:"$1"
,
{
:const
,
name
}}],
[
:"$_"
]}]
case
:dets
.
select
(
file
(
"db"
),
spec
)
do
[
account
]
->
from_tuple
(
account
)
_
->
nil
end
end
def
get_meta
(
%
__MODULE__
{
id
:
id
},
key
,
default
\\
nil
)
do
case
:dets
.
lookup
(
file
(
"meta"
),
{
id
,
key
})
do
[{
_
,
value
}]
->
(
value
||
default
)
_
->
default
end
end
@spec
find_meta_accounts
(
String
.
t
())
::
[{
account
::
t
(),
value
::
String
.
t
()},
...
]
@doc
"Find all accounts that have a meta of `key`."
def
find_meta_accounts
(
key
)
do
spec
=
[{{{
:"$1"
,
:"$2"
},
:"$3"
},
[{
:==
,
:"$2"
,
{
:const
,
key
}}],
[{{
:"$1"
,
:"$3"
}}]}]
for
{
id
,
val
}
<-
:dets
.
select
(
file
(
"meta"
),
spec
),
do
:
{
get
(
id
),
val
}
end
@doc
"Find an account given a specific meta `key` and `value`."
@spec
find_meta_account
(
String
.
t
(),
String
.
t
())
::
t
()
|
nil
def
find_meta_account
(
key
,
value
)
do
#spec = [{{{:"$1", :"$2"}, :"$3"}, [:andalso, {:==, :"$2", {:const, key}}, {:==, :"$3", {:const, value}}], [:"$1"]}]
spec
=
[{{{
:"$1"
,
:"$2"
},
:"$3"
},
[{
:andalso
,
{
:==
,
:"$2"
,
{
:const
,
key
}},
{
:==
,
{
:const
,
value
},
:"$3"
}}],
[
:"$1"
]}]
case
:dets
.
select
(
file
(
"meta"
),
spec
)
do
[
id
]
->
get
(
id
)
_
->
nil
end
end
def
get_all_meta
(
%
__MODULE__
{
id
:
id
})
do
spec
=
[{{{
:"$1"
,
:"$2"
},
:"$3"
},
[{
:==
,
:"$1"
,
{
:const
,
id
}}],
[{{
:"$2"
,
:"$3"
}}]}]
:dets
.
select
(
file
(
"meta"
),
spec
)
end
def
put_user_meta
(
account
=
%
__MODULE__
{},
key
,
value
)
do
put_meta
(
account
,
"u:"
<>
key
,
value
)
end
def
put_meta
(
%
__MODULE__
{
id
:
id
},
key
,
value
)
do
:dets
.
insert
(
file
(
"meta"
),
{{
id
,
key
},
value
})
end
def
delete_meta
(
%
__MODULE__
{
id
:
id
},
key
)
do
:dets
.
delete
(
file
(
"meta"
),
{
id
,
key
})
end
def
all_accounts
()
do
:dets
.
traverse
(
file
(
"db"
),
fn
(
obj
)
->
{
:continue
,
from_tuple
(
obj
)}
end
)
end
def
all_predicates
()
do
:dets
.
traverse
(
file
(
"predicates"
),
fn
(
obj
)
->
{
:continue
,
obj
}
end
)
end
def
all_meta
()
do
:dets
.
traverse
(
file
(
"meta"
),
fn
(
obj
)
->
{
:continue
,
obj
}
end
)
end
def
merge_account
(
old_id
,
new_id
)
do
if
old_id
!=
new_id
do
spec
=
[{{
:"$1"
,
:"$2"
},
[{
:==
,
:"$2"
,
{
:const
,
old_id
}}],
[
:"$1"
]}]
predicates
=
:dets
.
select
(
file
(
"predicates"
),
spec
)
for
pred
<-
predicates
,
do
:
:ok
=
:dets
.
insert
(
file
(
"predicates"
),
{
pred
,
new_id
})
spec
=
[{{{
:"$1"
,
:"$2"
},
:"$3"
},
[{
:==
,
:"$1"
,
{
:const
,
old_id
}}],
[{{
:"$2"
,
:"$3"
}}]}]
metas
=
:dets
.
select
(
file
(
"meta"
),
spec
)
for
{
k
,
v
}
<-
metas
do
:dets
.
delete
(
file
(
"meta"
),
{{
old_id
,
k
}})
:ok
=
:dets
.
insert
(
file
(
"meta"
),
{{
new_id
,
k
},
v
})
end
:dets
.
delete
(
file
(
"db"
),
old_id
)
IRC.Membership
.
merge_account
(
old_id
,
new_id
)
IRC.UserTrack
.
merge_account
(
old_id
,
new_id
)
IRC.Connection
.
dispatch
(
"account"
,
{
:account_change
,
old_id
,
new_id
})
IRC.Connection
.
dispatch
(
"conn"
,
{
:account_change
,
old_id
,
new_id
})
end
:ok
end
@doc
"Find an account by a logged in user"
def
find_by_nick
(
network
,
nick
)
do
do_lookup
(%
ExIRC.SenderInfo
{
nick
:
nick
,
network
:
network
},
false
)
end
@doc
"Always find an account by nickname, even if offline. Uses predicates and then account name."
def
find_always_by_nick
(
network
,
chan
,
nick
)
do
with
\
nil
<-
find_by_nick
(
network
,
nick
),
nil
<-
do_lookup
(%
User
{
network
:
network
,
nick
:
nick
},
false
),
nil
<-
get_by_name
(
nick
)
do
nil
else
%
__MODULE__
{}
=
account
->
memberships
=
IRC.Membership
.
of_account
(
account
)
if
Enum
.
any?
(
memberships
,
fn
({
net
,
ch
})
->
(
net
==
network
)
or
(
chan
&&
chan
==
ch
)
end
)
do
account
else
nil
end
end
end
def
find
(
something
)
do
do_lookup
(
something
,
false
)
end
def
lookup
(
something
,
make_default
\\
true
)
do
account
=
do_lookup
(
something
,
make_default
)
if
account
&&
Map
.
get
(
something
,
:nick
)
do
IRC.Connection
.
dispatch
(
"account"
,
{
:account_auth
,
Map
.
get
(
something
,
:nick
),
account
.
id
})
end
account
end
def
handle_info
(
_
,
state
)
do
{
:noreply
,
state
}
end
def
handle_cast
(
_
,
state
)
do
{
:noreply
,
state
}
end
def
handle_call
(
_
,
_
,
state
)
do
{
:noreply
,
state
}
end
def
terminate
(
_
,
state
)
do
for
{
_
,
dets
}
<-
state
do
:dets
.
sync
(
dets
)
:dets
.
close
(
dets
)
end
end
defp
do_lookup
(
message
=
%
IRC.Message
{
account
:
account_id
},
make_default
)
when
is_binary
(
account_id
)
do
get
(
account_id
)
end
defp
do_lookup
(
sender
=
%
ExIRC.Who
{},
make_default
)
do
if
user
=
IRC.UserTrack
.
find_by_nick
(
sender
)
do
lookup
(
user
,
make_default
)
else
#FIXME this will never work with continued lookup by other methods as Who isn't compatible
lookup_by_nick
(
sender
,
:dets
.
lookup
(
file
(
"predicates"
),
{
sender
.
network
,{
:nick
,
sender
.
nick
}}),
make_default
)
end
end
defp
do_lookup
(
sender
=
%
ExIRC.SenderInfo
{},
make_default
)
do
lookup
(
IRC.UserTrack
.
find_by_nick
(
sender
),
make_default
)
end
defp
do_lookup
(
user
=
%
User
{
account
:
id
},
make_default
)
when
is_binary
(
id
)
do
get
(
id
)
end
defp
do_lookup
(
user
=
%
User
{
network
:
server
,
nick
:
nick
},
make_default
)
do
lookup_by_nick
(
user
,
:dets
.
lookup
(
file
(
"predicates"
),
{
server
,{
:nick
,
nick
}}),
make_default
)
end
defp
do_lookup
(
nil
,
_
)
do
nil
end
defp
lookup_by_nick
(
_
,
[{
_
,
id
}],
_make_default
)
do
get
(
id
)
end
defp
lookup_by_nick
(
user
,
_
,
make_default
)
do
#authenticate_by_host(user)
if
make_default
,
do
:
new_account
(
user
),
else
:
nil
end
def
new_account
(
nick
)
do
id
=
EntropyString
.
large_id
()
:dets
.
insert
(
file
(
"db"
),
{
id
,
nick
,
EntropyString
.
token
()})
get
(
id
)
end
def
new_account
(%{
nick
:
nick
,
network
:
server
})
do
id
=
EntropyString
.
large_id
()
:dets
.
insert
(
file
(
"db"
),
{
id
,
nick
,
EntropyString
.
token
()})
:dets
.
insert
(
file
(
"predicates"
),
{{
server
,
{
:nick
,
nick
}},
id
})
get
(
id
)
end
def
update_account_name
(
account
=
%
__MODULE__
{
id
:
id
},
name
)
do
account
=
%
__MODULE__
{
account
|
name
:
name
}
:dets
.
insert
(
file
(
"db"
),
from_struct
(
account
))
get
(
id
)
end
def
get_predicates
(
%
__MODULE__
{}
=
account
)
do
spec
=
[{{
:"$1"
,
:"$2"
},
[{
:==
,
:"$2"
,
{
:const
,
account
.
id
}}],
[
:"$1"
]}]
:dets
.
select
(
file
(
"predicates"
),
spec
)
end
end
File Metadata
Details
Attached
Mime Type
text/x-ruby
Expires
Sat, Feb 28, 11:54 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
87471
Default Alt Text
account.ex (7 KB)
Attached To
rNOLA Nola
Event Timeline
Log In to Comment