1 import os
, sys
, re
, string
, logging
, time
2 import random
, anydbm
, smtplib
4 ALL_STORIES
= set(range(1, 101+1))
5 WWW_URL
= "http://nearfar.org/dailyzen/"
6 UNSUBSCRIBE_URL
= WWW_URL
+ "do.cgi/unsubscribe/%s,%s"
7 VERIFICATION_URL
= WWW_URL
+ "do.cgi/verify/%s,%s"
8 DBFILE
= "/home/protected/data/dailyzen.db"
10 # TODO: unused (From: is same as To:). do something about it.
11 FROM_ADDR
= "mail@nearfar.org"
13 # Decorator: Call the function once, and return the same return value always.
18 ## Logging, error handling bits
20 # FIXME: is call_once necessary?
22 def logging_handler():
23 "Return the desired and default logging handler"
24 hdlr
= logging
.FileHandler("/home/protected/logs/dailyzen.log")
25 formatter
= logging
.Formatter("%(asctime)s %(levelname)s %(message)s")
26 hdlr
.setFormatter(formatter
)
29 dz
= logging
.getLogger("dailyzen")
30 dz
.addHandler(logging_handler())
31 dz
.setLevel(logging
.DEBUG
)
33 class UserError(Exception):
37 ## Register/Validate mechanism
39 def register_user(email
):
40 """Just register the user giving him (via email) an unique url
41 to valid himself against the given email address.
43 On clicking the URL, `validate_user' function would be called."""
44 EMAIL_MSG
= r
"""Hey there,
46 Just to make sure that it is really you, please click on this link.
49 You will then be receiving your daily Zen story shortly.
51 PS: Not initiated by you? simply ignore this email.
54 db
= anydbm
.open(DBFILE
, "c")
55 if email
in db
.keys():
56 raise UserError
, "email address already found"
57 user
= dict(email
=email
, id=random_string(), sent_stories
=[], valid
=False)
58 db
[email
] = repr(user
)
61 verification_url
= VERIFICATION_URL
% (email
, user
["id"])
62 send_email(email
, [email
], "DailyZen Verification", EMAIL_MSG
% verification_url
)
63 dz
.info("Verification code sent to %s", email
)
65 def validate_user(email
, id):
66 db
= anydbm
.open(DBFILE
)
67 if email
in db
.keys():
68 user
= eval(db
[email
])
70 if user
['id'] == id and user
['valid'] is False:
74 dz
.info("Validated %s", email
)
76 raise UserError
, "Invalid request"
80 raise UserError
, "Email address (%s) not found" % email
82 def delete_user(email
, id):
83 db
= anydbm
.open(DBFILE
, "w")
84 if email
in db
.keys():
85 user
= eval(db
[email
])
89 raise UserError
, "Invalid request"
91 raise UserError
, "Email address not found (Perhaps already unsubscribed?)"
93 def random_string(length
=15):
94 char_domain
= string
.ascii_letters
+ string
.digits
96 for x
in range(length
):
97 id.append(random
.choice(char_domain
))
104 db
= anydbm
.open(DBFILE
, "c")
113 def write_user(user
):
114 db
= anydbm
.open(DBFILE
, "w")
115 db
[user
['email']] = repr(user
)
119 def get_story(number
):
120 f
= open("101zenstories/%d.html.txt" % number
)
121 title
= f
.readline().strip()
122 f
.readline() # empty line
126 def send_email(from_
, toaddrs
, subject
, body
):
127 SENDMAIL
= "sendmail" # sendmail location
128 p
= os
.popen("%s -t" % SENDMAIL
, "w")
129 p
.write("From: %s\n" % from_
)
130 p
.write("To: %s\n" % ",".join(toaddrs
))
131 p
.write("Subject: %s\n" % subject
)
135 if sts
is not None and sts
!= 0:
136 dz
.error("sendmail failed with exit status: %d", sts
)
138 # not used: fails in nfshost
139 def send_email_SMTP(from_
, toaddrs
, subject
, body
):
140 msg
= "From: %s\nSubject: %s\n\n%s" % (from_
, subject
, body
)
141 s
= smtplib
.SMTP('127.0.0.1')
142 s
.sendmail(from_
, toaddrs
, msg
)
145 def send_random_story_to_user(user
):
146 sent_stories
= set(user
['sent_stories'])
147 unsent_stories
= ALL_STORIES
.difference(sent_stories
)
148 if len(unsent_stories
) == 0:
149 # nothing left for this user
150 # TODO: should we remove him from db?
153 if len(sent_stories
) == 0:
154 # send his first story
155 notice_msg
= "Welcome. Here is your first story. From now on, you would get a random story that you were not sent before.\n---\n\n"
159 next
= random
.choice(list(unsent_stories
))
160 user
["sent_stories"].append(next
)
161 title
, story
= get_story(next
)
162 email
= user
["email"]
164 unsubscribe_url
= UNSUBSCRIBE_URL
% (user
["email"], user
["id"])
165 signature
= "\n\n----\n%s\n\nTo unsubsubcribe,\n%s" % (WWW_URL
, unsubscribe_url
)
166 send_email(email
, [email
], title
, notice_msg
+ story
+ signature
)
167 dz
.debug("email: To=%s %s (%d)", email
, title
, next
)
171 return long(time
.time())
173 def secs(n
): return n
174 def mins(n
): return 60*secs(n
)
175 def hrs(n
): return 60*mins(n
)
176 def days(n
): return 24*hrs(n
)
179 dz
.info("Broadcast operation started")
180 for user
in read_users():
181 send_random_story_to_user(user
)
185 if __name__
== '__main__':
189 write_user(dict(email
="srid@nearfar.org", id="12345", sent_stories
=[1,6,23], valid
=True))
190 # write_user(dict(email="foo@example.com", id="84854", sent_stories=[51,78,99, 2, 3]))