- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我们计划将驻留在 parse.com 上的聊天服务器应用程序移动到 Google 应用引擎的数据存储区,因为 parse 将于 2017 年 1 月关闭其服务。我认为这应该可以通过 App engine's XMPP API 实现。 .不确定,我很想听听你的意见..
目前我正在使用 Google 提供的代码进行测试
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Crowdguru sample application using the XMPP service on Google App Engine."""
import datetime
from google.appengine.api import datastore_types
from google.appengine.api import xmpp
from google.appengine.ext import ndb
from google.appengine.ext.webapp import xmpp_handlers
import webapp2
from webapp2_extras import jinja2
PONDER_MSG = 'Hmm. Let me think on that a bit.'
TELLME_MSG = 'While I\'m thinking, perhaps you can answer me this: {}'
SOMEONE_ANSWERED_MSG = ('We seek those who are wise and fast. One out of two '
'is not enough. Another has answered my question.')
ANSWER_INTRO_MSG = 'You asked me: {}'
ANSWER_MSG = 'I have thought long and hard, and concluded: {}'
WAIT_MSG = ('Please! One question at a time! You can ask me another once you '
'have an answer to your current question.')
THANKS_MSG = 'Thank you for your wisdom.'
TELLME_THANKS_MSG = THANKS_MSG + ' I\'m still thinking about your question.'
EMPTYQ_MSG = 'Sorry, I don\'t have anything to ask you at the moment.'
HELP_MSG = ('I am the amazing Crowd Guru. Ask me a question by typing '
'\'/tellme the meaning of life\', and I will answer you forthwith! '
'To learn more, go to {}/')
MAX_ANSWER_TIME = 120
class IMProperty(ndb.StringProperty):
"""A custom property for handling IM objects.
IM or Instant Message objects include both an address and its protocol. The
constructor and __str__ method on these objects allow easy translation from
type string to type datastore_types.IM.
"""
def _validate(self, value):
"""Validator to make sure value is an instance of datastore_types.IM.
Args:
value: The value to be validated. Should be an instance of
datastore_types.IM.
Raises:
TypeError: If value is not an instance of datastore_types.IM.
"""
if not isinstance(value, datastore_types.IM):
raise TypeError('expected an IM, got {!r}'.format(value))
def _to_base_type(self, value):
"""Converts native type (datastore_types.IM) to datastore type (string).
Args:
value: The value to be converted. Should be an instance of
datastore_types.IM.
Returns:
String corresponding to the IM value.
"""
return str(value)
def _from_base_type(self, value):
"""Converts datastore type (string) to native type (datastore_types.IM).
Args:
value: The value to be converted. Should be a string.
Returns:
String corresponding to the IM value.
"""
return datastore_types.IM(value)
class Question(ndb.Model):
"""Model to hold questions that the Guru can answer."""
question = ndb.TextProperty(required=True)
asker = IMProperty(required=True)
asked = ndb.DateTimeProperty(required=True, auto_now_add=True)
suspended = ndb.BooleanProperty(required=True)
assignees = IMProperty(repeated=True)
last_assigned = ndb.DateTimeProperty()
answer = ndb.TextProperty(indexed=True)
answerer = IMProperty()
answered = ndb.DateTimeProperty()
@staticmethod
@ndb.transactional
def _try_assign(key, user, expiry):
"""Assigns and returns the question if it's not assigned already.
Args:
key: ndb.Key: The key of a Question to try and assign.
user: datastore_types.IM: The user to assign the question to.
expiry: datetime.datetime: The expiry date of the question.
Returns:
The Question object. If it was already assigned, no change is made.
"""
question = key.get()
if not question.last_assigned or question.last_assigned < expiry:
question.assignees.append(user)
question.last_assigned = datetime.datetime.now()
question.put()
return question
@classmethod
def assign_question(cls, user):
"""Gets an unanswered question and assigns it to a user to answer.
Args:
user: datastore_types.IM: The identity of the user to assign a
question to.
Returns:
The Question entity assigned to the user, or None if there are no
unanswered questions.
"""
question = None
while question is None or user not in question.assignees:
# Assignments made before this timestamp have expired.
expiry = (datetime.datetime.now()
- datetime.timedelta(seconds=MAX_ANSWER_TIME))
# Find a candidate question
query = cls.query(cls.answerer == None, cls.last_assigned < expiry)
# If a question has never been assigned, order by when it was asked
query = query.order(cls.last_assigned, cls.asked)
candidates = [candidate for candidate in query.fetch(2)
if candidate.asker != user]
if not candidates:
# No valid questions in queue.
break
# Try and assign it
question = cls._try_assign(candidates[0].key, user, expiry)
# Expire the assignment after a couple of minutes
return question
@ndb.transactional
def unassign(self, user):
"""Unassigns the given user from this question.
Args:
user: datastore_types.IM: The user who will no longer be answering
this question.
"""
question = self.key.get()
if user in question.assignees:
question.assignees.remove(user)
question.put()
@classmethod
def get_asked(cls, user):
"""Returns the user's outstanding asked question, if any.
Args:
user: datastore_types.IM: The identity of the user asking.
Returns:
An unanswered Question entity asked by the user, or None if there
are no unanswered questions.
"""
query = cls.query(cls.asker == user, cls.answer == None)
return query.get()
@classmethod
def get_answering(cls, user):
"""Returns the question the user is answering, if any.
Args:
user: datastore_types.IM: The identity of the user answering.
Returns:
An unanswered Question entity assigned to the user, or None if there
are no unanswered questions.
"""
query = cls.query(cls.assignees == user, cls.answer == None)
return query.get()
def bare_jid(sender):
"""Identify the user by bare jid.
See http://wiki.xmpp.org/web/Jabber_Resources for more details.
Args:
sender: String; A jabber or XMPP sender.
Returns:
The bare Jabber ID of the sender.
"""
return sender.split('/')[0]
class XmppHandler(xmpp_handlers.CommandHandler):
"""Handler class for all XMPP activity."""
def unhandled_command(self, message=None):
"""Shows help text for commands which have no handler.
Args:
message: xmpp.Message: The message that was sent by the user.
"""
message.reply(HELP_MSG.format(self.request.host_url))
def askme_command(self, message=None):
"""Responds to the /askme command.
Args:
message: xmpp.Message: The message that was sent by the user.
"""
im_from = datastore_types.IM('xmpp', bare_jid(message.sender))
currently_answering = Question.get_answering(im_from)
question = Question.assign_question(im_from)
if question:
message.reply(TELLME_MSG.format(question.question))
else:
message.reply(EMPTYQ_MSG)
# Don't unassign their current question until we've picked a new one.
if currently_answering:
currently_answering.unassign(im_from)
def text_message(self, message=None):
"""Called when a message not prefixed by a /cmd is sent to the XMPP bot.
Args:
message: xmpp.Message: The message that was sent by the user.
"""
im_from = datastore_types.IM('xmpp', bare_jid(message.sender))
question = Question.get_answering(im_from)
if question:
other_assignees = question.assignees
other_assignees.remove(im_from)
# Answering a question
question.answer = message.arg
question.answerer = im_from
question.assignees = []
question.answered = datetime.datetime.now()
question.put()
# Send the answer to the asker
xmpp.send_message([question.asker.address],
ANSWER_INTRO_MSG.format(question.question))
xmpp.send_message([question.asker.address],
ANSWER_MSG.format(message.arg))
# Send acknowledgement to the answerer
asked_question = Question.get_asked(im_from)
if asked_question:
message.reply(TELLME_THANKS_MSG)
else:
message.reply(THANKS_MSG)
# Tell any other assignees their help is no longer required
if other_assignees:
xmpp.send_message([user.address for user in other_assignees],
SOMEONE_ANSWERED_MSG)
else:
self.unhandled_command(message)
def tellme_command(self, message=None):
"""Handles /tellme requests, asking the Guru a question.
Args:
message: xmpp.Message: The message that was sent by the user.
"""
im_from = datastore_types.IM('xmpp', bare_jid(message.sender))
asked_question = Question.get_asked(im_from)
if asked_question:
# Already have a question
message.reply(WAIT_MSG)
else:
# Asking a question
asked_question = Question(question=message.arg, asker=im_from)
asked_question.put()
currently_answering = Question.get_answering(im_from)
if not currently_answering:
# Try and find one for them to answer
question = Question.assign_question(im_from)
if question:
message.reply(TELLME_MSG.format(question.question))
return
message.reply(PONDER_MSG)
class XmppPresenceHandler(webapp2.RequestHandler):
"""Handler class for XMPP status updates."""
def post(self, status):
"""POST handler for XMPP presence.
Args:
status: A string which will be either available or unavailable
and will indicate the status of the user.
"""
sender = self.request.get('from')
im_from = datastore_types.IM('xmpp', bare_jid(sender))
suspend = (status == 'unavailable')
query = Question.query(Question.asker == im_from,
Question.answer == None,
Question.suspended == (not suspend))
question = query.get()
if question:
question.suspended = suspend
question.put()
class LatestHandler(webapp2.RequestHandler):
"""Displays the most recently answered questions."""
@webapp2.cached_property
def jinja2(self):
"""Cached property holding a Jinja2 instance.
Returns:
A Jinja2 object for the current app.
"""
return jinja2.get_jinja2(app=self.app)
def render_response(self, template, **context):
"""Use Jinja2 instance to render template and write to output.
Args:
template: filename (relative to $PROJECT/templates) that we are
rendering.
context: keyword arguments corresponding to variables in template.
"""
rendered_value = self.jinja2.render_template(template, **context)
self.response.write(rendered_value)
def get(self):
"""Handler for latest questions page."""
query = Question.query(Question.answered > None).order(
-Question.answered)
self.render_response('latest.html', questions=query.fetch(20))
APPLICATION = webapp2.WSGIApplication([
('/', LatestHandler),
('/_ah/xmpp/message/chat/', XmppHandler),
('/_ah/xmpp/presence/(available|unavailable)/', XmppPresenceHandler),
], debug=True)
如果用户选择另一个他想与之聊天的用户,则应调用 API url /_ah/xmpp/message/chat/
,它会自动调用 XmppHandler
处理程序。
('/_ah/xmpp/message/chat/', XmppHandler)
我的疑问是,如果他在那个特定的聊天中发布像 foo
这样的消息,它会自动调用 XmppHandler
中存在的 text_message
方法吗?我们是否也需要在客户端配置 xmpp?
最佳答案
对于客户端 api 兼容和数据库迁移,您可以托管自己的解析服务器。
有一个简单的 express 项目可以使用 parse-server。 https://github.com/ParsePlatform/parse-server-example
他们有很多针对每个云平台的部署指南
或者您可以使用您的域名托管您的 nodejs 服务器。
如果你想做一些不同于解析的事情,你可以发送一个拉取请求到parse-server . LiveQuery是贡献者创建的额外功能。
有关更多详细信息,请参阅来自 Parse.com 的链接, github wiki , 和 community links .
关于android - 将聊天服务器应用程序从 parse.com 移动到 google app engine,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37433212/
谁能解释一下 Server.MapPath(".")、Server.MapPath("~")、Server.MapPath(@"之间的区别\") 和 Server.MapPath("/")? 最佳答案
我不知道,为什么我们要使用 Server.UrlEncode() & Server.UrlDecode()?!在 QueryString 中我们看到 URL 中的任何内容,那么为什么我们要对它们进行编
我已经通过 WHM 在我的一个域上安装了 ssl 证书。网站正在使用 https://xyz.com . 但是它不适用于 https://www.xyz.com .我已经检查了证书,它也适用于 www
我已经使用 WMI 检测操作系统上是否存在防病毒软件,itz 正常工作并通过使用命名空间向我显示防病毒信息,例如 win xp 和 window7 上的名称和实例 ID:\root\SecurityC
我们有 hive 0.10 版本,我们想知道是否应该使用 Hive Server 1 或 Hive Server2。另一个问题是连接到在端口 10000 上运行的 Hive 服务器,使用 3rd 方工
我想在 C++ 中使用 Windows Server API 设置一个 HTTPS 服务器,我使用了示例代码,它在 HTTP 上工作正常,但我就是不能让它在 HTTPS 上工作。 (我不想要客户端 S
我写了一个非常基本的类来发送电子邮件。我用 smtp 服务器对其进行了测试,它工作正常,但是当我尝试使用我公司的交换服务器时,它给出了这个异常: SMTP 服务器需要安全连接或客户端未通过身份验证。服
我的应用程序包含一个“网关”DataSnap REST 服务器,它是所有客户端的第一个访问点。根据客户端在请求中传递的用户名(基本身份验证),请求需要重定向到另一个 DataSnap 服务器。我的问题
我有一个 Tomcat 服务器和一个 Glassfish4 服务器。我的 Servlet 在 Tomcat 服务器上启动得很好,但在 Glassfish4 服务器上给我一个“HTTP Status 4
我在 vmware 上创建了一个 ubuntu 服务器。我用它作为文件服务器。如果我通过托管虚拟机的计算机进行连接,则可以访问它。我无法从同一网络上的其他计算机执行此操作。提前致谢! 最佳答案 首先确
如何重启 Rails 服务器?我从 开始 rails server -d 所以服务器是分离的 我知道的唯一方法就是去做ps 辅助 | grep rails 并 kill -9关于过程#但是像这样杀死进
我实际上正在尝试找到编写一个简单的 XMPP 服务器的最佳方法,或者找到一个占用空间非常小的服务器。我只关心XMPP的核心功能(状态、消息传递、群组消息传递)。目前还在学习 XMPP 协议(proto
我实际上正在尝试找到编写简单 XMPP 服务器的最佳方法,或者找到一个占用空间非常小的方法。我只关心 XMPP 的核心功能(统计、消息、组消息)。目前也在学习 XMPP 协议(protocol),所以
我们正在尝试从 Java JAX-RS 适配器访问 SOAP 1.1 Web 服务。 我们正在使用从 WSDL 生成的 SOAP 客户端。 但是当解码 SOAP 故障时,我们得到以下异常: ... C
目前,我和许多其他人正在多个平台(Windows、OS X 和可能的 Linux)上使用 Python HTTP 服务器。我们正在使用 Python HTTP 服务器来测试 JavaScript 游戏
我有一个连续运行的服务器程序(C#/.NET 2.0 on Linux with mono),我想从 PHP 脚本连接到它以在网站上显示状态信息。 目的是创建一个(某种)实时浏览器游戏(无 Flash
所以我有一个单页客户端应用程序。 正常流程: 应用程序 -> OAuth2 服务器 -> 应用程序 我们有自己的 OAuth2 服务器,因此人们可以登录应用程序并获取与用户实体关联的 access_t
我们刚刚将测试 Web 服务器从 Server 2008 升级到 Server 2012 R2。我们有一个部署我们网站的批处理脚本。当它将站点推送到服务器时,它现在失败了。奇怪的是,我可以使用相同的发
建议一些加载SpagoBI服务器的方法,我尝试了所有方法来解析spagobi服务器。在 Catalina 中,错误是 - * SEVERE: Unable to process Jar entry [
当我们点击应用程序服务器(apache tomcat)时,它会创建一个线程来处理我们的请求并与 tomcat 连接,建立连接,tomcat 创建另一个线程来处理请求并将其传递给连接,连接线程将其传递给
我是一名优秀的程序员,十分优秀!