]> gitweb.michael.orlitzky.com - dead/halcyon.git/blob - src/Twitter/Status.hs
Add referenced users' timeline URLs to the bottom of each message.
[dead/halcyon.git] / src / Twitter / Status.hs
1 -- |Functions and data for working with Twitter statuses.
2 module Twitter.Status
3 where
4
5 import Data.Maybe
6 import Data.String.Utils (join, splitWs)
7 import Test.HUnit
8 import Text.Regex (matchRegex, mkRegex)
9 import Text.XML.HaXml
10
11 import StringUtils (listify)
12 import Twitter.User
13 import Twitter.Xml
14
15 -- |Represents one Twitter status. We don't care about any of their
16 -- other properties.
17 data Status = Status { status_id :: Integer,
18 created_at :: String,
19 text :: String,
20 user :: User }
21 deriving (Show, Eq)
22
23
24 -- |Given some XML content, create a 'Status' from it.
25 status_from_content :: Content -> (Maybe Status)
26 status_from_content content =
27
28 if (length status_ids) == 0
29 || (length created_ats) == 0
30 || (length texts) == 0
31 || (length users) == 0
32 then
33 Nothing
34 else
35 case first_status_id of
36 Nothing -> Nothing
37 (Just status_id_data) ->
38 case first_created_at of
39 Nothing -> Nothing
40 (Just created_at_data) ->
41 case first_user of
42 Nothing -> Nothing
43 (Just user_object) ->
44 case (reads status_id_data :: [(Integer, String)]) of
45 [] -> Nothing
46 parseresult:_ -> Just (Status (fst parseresult) created_at_data all_text user_object)
47
48 where
49 status_ids = (unique_id content)
50 first_status_id = get_char_data (status_ids !! 0)
51
52 created_ats = (status_created_at content)
53 first_created_at = get_char_data (created_ats !! 0)
54
55 texts = (status_text content)
56 all_text = concat $ catMaybes (map get_char_data texts)
57
58 users = (status_user content)
59 first_user = user_from_content (users !! 0)
60
61
62 -- |Takes an XML String as an argument, and returns the
63 -- status that was parsed from it. Should only be used
64 -- on XML string where a <status> is a top-level element.
65 parse_status :: String -> [Status]
66 parse_status xml_data =
67 catMaybes maybe_status
68 where
69 (Document _ _ root _) = xmlParse xml_file_name xml_data
70 root_elem = CElem root
71 status_element = (single_status root_elem)
72 maybe_status = map status_from_content status_element
73
74
75 -- |Takes an XML String as an argument, and returns the list of
76 -- statuses that can be parsed from it.
77 parse_statuses :: String -> [Status]
78 parse_statuses xml_data =
79 catMaybes maybe_statuses
80 where
81 (Document _ _ root _) = xmlParse xml_file_name xml_data
82 root_elem = CElem root
83 status_elements = (all_statuses root_elem)
84 maybe_statuses = map status_from_content status_elements
85
86
87 -- |This is a required parameter to the xmlParse function used in
88 -- error reporting. We're not parsing a function, though, so we leave
89 -- it blank.
90 xml_file_name :: String
91 xml_file_name = ""
92
93 -- |Returns a nicely-formatted String representing the given 'Status'
94 -- object.
95 pretty_print :: Status -> String
96 pretty_print status =
97 concat [ name,
98 " - ",
99 (created_at status),
100 "\n",
101 replicate ((length name) + 3 + (length (created_at status))) '-',
102 "\n",
103 replace_entities (text status),
104 "\n\n",
105 join "\n" user_timeline_urls,
106 "\n" ]
107 where
108 name = screen_name (user status)
109 user_timeline_urls = listify (make_user_timeline_urls status)
110
111
112 -- |Given a list of statuses, returns the greatest status_id belonging
113 -- to one of the statuses in the list.
114 get_max_status_id :: [Status] -> Integer
115 get_max_status_id statuses =
116 maximum status_ids
117 where
118 status_ids = map status_id statuses
119
120
121 -- |Parse one username from a word.
122 parse_username :: String -> Maybe String
123 parse_username word =
124 case matches of
125 Nothing -> Nothing
126 Just [] -> Nothing
127 Just (first_match:_) -> Just first_match
128 where
129 username_regex = mkRegex "@([a-zA-Z0-9_]+)"
130 matches = matchRegex username_regex word
131
132
133 -- |Parse all usernames of the form @username from a status.
134 parse_usernames_from_status :: Status -> [String]
135 parse_usernames_from_status status =
136 catMaybes (map parse_username status_words)
137 where
138 status_words = splitWs (text status)
139
140 -- |Get all referenced users' timeline URLs.
141 make_user_timeline_urls :: Status -> [String]
142 make_user_timeline_urls status =
143 map screen_name_to_timeline_url usernames
144 where
145 usernames = parse_usernames_from_status status
146
147
148 status_tests :: [Test]
149 status_tests = [ test_parse_usernames ]
150
151
152 test_parse_usernames :: Test
153 test_parse_usernames =
154 TestCase $ assertEqual "All usernames are parsed." expected_usernames actual_usernames
155 where
156 dummy_user = User { screen_name = "nobody" }
157 dummy_status = Status { status_id = 1,
158 created_at = "never",
159 text = "Hypothesis: @donsbot and @bonus500 are two personalities belonging to the same person.",
160 user = dummy_user }
161
162 actual_usernames = parse_usernames_from_status dummy_status
163 expected_usernames = ["donsbot", "bonus500"]