zilencer: Move feedback code to zerver/lib/feedback.

This commit is contained in:
Tim Abbott 2017-03-05 23:35:57 -08:00
parent 1f9d93bc96
commit ed5b76f566
3 changed files with 82 additions and 73 deletions

75
zerver/lib/feedback.py Normal file
View File

@ -0,0 +1,75 @@
from __future__ import absolute_import
from typing import Any, Dict, Optional, Text
from zerver.lib.actions import internal_send_message
from zerver.lib.redis_utils import get_redis_client
from zerver.models import get_realm, get_user_profile_by_email, \
get_realm_by_email_domain, UserProfile, Realm
import time
client = get_redis_client()
def realm_for_email(email):
# type: (str) -> Optional[Realm]
try:
user = get_user_profile_by_email(email)
return user.realm
except UserProfile.DoesNotExist:
pass
return get_realm_by_email_domain(email)
def has_enough_time_expired_since_last_message(sender_email, min_delay):
# type: (Text, float) -> bool
# This function returns a boolean, but it also has the side effect
# of noting that a new message was received.
key = 'zilencer:feedback:%s' % (sender_email,)
t = int(time.time())
last_time = client.getset(key, t)
if last_time is None:
return True
delay = t - int(last_time)
return delay > min_delay
def get_ticket_number():
# type: () -> int
num_file = '/var/tmp/.feedback-bot-ticket-number'
try:
ticket_number = int(open(num_file).read()) + 1
except Exception:
ticket_number = 1
open(num_file, 'w').write('%d' % (ticket_number,))
return ticket_number
def deliver_feedback_by_zulip(message):
# type: (Dict[str, Any]) -> None
subject = "%s" % (message["sender_email"],)
if len(subject) > 60:
subject = subject[:57].rstrip() + "..."
content = u''
sender_email = message['sender_email']
# We generate ticket numbers if it's been more than a few minutes
# since their last message. This avoids some noise when people use
# enter-send.
need_ticket = has_enough_time_expired_since_last_message(sender_email, 180)
if need_ticket:
ticket_number = get_ticket_number()
content += '\n~~~'
content += '\nticket Z%03d (@support please ack)' % (ticket_number,)
content += '\nsender: %s' % (message['sender_full_name'],)
content += '\nemail: %s' % (sender_email,)
if 'sender_domain' in message:
content += '\nrealm: %s' % (message['sender_domain'],)
content += '\n~~~'
content += '\n\n'
content += message['content']
internal_send_message(realm_for_email("feedback@zulip.com"), "feedback@zulip.com",
"stream", "support", subject, content)

View File

@ -1,10 +1,12 @@
from django.conf.urls import url, include from django.conf.urls import url, include
from typing import Any
from zerver.lib.rest import rest_dispatch from zerver.lib.rest import rest_dispatch
import zilencer.views import zilencer.views
import zerver.views.report import zerver.views.report
i18n_urlpatterns = [ i18n_urlpatterns = [] # type: Any
]
# Zilencer views following the REST API style # Zilencer views following the REST API style
v1_api_and_json_patterns = [ v1_api_and_json_patterns = [

View File

@ -6,87 +6,19 @@ from django.http import HttpResponse, HttpRequest
from zilencer.models import Deployment from zilencer.models import Deployment
from zerver.decorator import has_request_variables, REQ from zerver.decorator import has_request_variables, REQ
from zerver.lib.actions import internal_send_message
from zerver.lib.error_notify import do_report_error from zerver.lib.error_notify import do_report_error
from zerver.lib.redis_utils import get_redis_client from zerver.lib.feedback import deliver_feedback_by_zulip
from zerver.lib.response import json_error, json_response
from zerver.lib.validator import check_dict from zerver.lib.validator import check_dict
from zerver.models import get_realm, get_user_profile_by_email, \
get_realm_by_email_domain, UserProfile, Realm
import time from typing import Any, Dict, Text
from typing import Dict, Optional, Any, Text
client = get_redis_client()
def has_enough_time_expired_since_last_message(sender_email, min_delay):
# type: (Text, float) -> bool
# This function returns a boolean, but it also has the side effect
# of noting that a new message was received.
key = 'zilencer:feedback:%s' % (sender_email,)
t = int(time.time())
last_time = client.getset(key, t)
if last_time is None:
return True
delay = t - int(last_time)
return delay > min_delay
def get_ticket_number():
# type: () -> int
num_file = '/var/tmp/.feedback-bot-ticket-number'
try:
ticket_number = int(open(num_file).read()) + 1
except Exception:
ticket_number = 1
open(num_file, 'w').write('%d' % (ticket_number,))
return ticket_number
@has_request_variables @has_request_variables
def submit_feedback(request, deployment, message=REQ(validator=check_dict([]))): def submit_feedback(request, deployment, message=REQ(validator=check_dict([]))):
# type: (HttpRequest, Deployment, Dict[str, Text]) -> HttpResponse # type: (HttpRequest, Deployment, Dict[str, Text]) -> HttpResponse
subject = "%s" % (message["sender_email"],) deliver_feedback_by_zulip(message)
if len(subject) > 60:
subject = subject[:57].rstrip() + "..."
content = u''
sender_email = message['sender_email']
# We generate ticket numbers if it's been more than a few minutes
# since their last message. This avoids some noise when people use
# enter-send.
need_ticket = has_enough_time_expired_since_last_message(sender_email, 180)
if need_ticket:
ticket_number = get_ticket_number()
content += '\n~~~'
content += '\nticket Z%03d (@support please ack)' % (ticket_number,)
content += '\nsender: %s' % (message['sender_full_name'],)
content += '\nemail: %s' % (sender_email,)
if 'sender_domain' in message:
content += '\nrealm: %s' % (message['sender_domain'],)
content += '\n~~~'
content += '\n\n'
content += message['content']
internal_send_message(realm_for_email("feedback@zulip.com"), "feedback@zulip.com",
"stream", "support", subject, content)
return HttpResponse(message['sender_email']) return HttpResponse(message['sender_email'])
@has_request_variables @has_request_variables
def report_error(request, deployment, type=REQ(), report=REQ(validator=check_dict([]))): def report_error(request, deployment, type=REQ(), report=REQ(validator=check_dict([]))):
# type: (HttpRequest, Deployment, Text, Dict[str, Any]) -> HttpResponse # type: (HttpRequest, Deployment, Text, Dict[str, Any]) -> HttpResponse
return do_report_error(deployment.name, type, report) return do_report_error(deployment.name, type, report)
def realm_for_email(email):
# type: (str) -> Optional[Realm]
try:
user = get_user_profile_by_email(email)
return user.realm
except UserProfile.DoesNotExist:
pass
return get_realm_by_email_domain(email)