gpt4 book ai didi

android - 将聊天服务器应用程序从 parse.com 移动到 google app engine

转载 作者:太空宇宙 更新时间:2023-11-03 13:15:22 25 4
gpt4 key购买 nike

我们计划将驻留在 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

他们有很多针对每个云平台的部署指南

Google App Engine

Heroku and mLab

AWS and Elastic Beanstalk

Digital Ocean

NodeChef

Microsoft Azure

Pivotal Web Services

Back4app

或者您可以使用您的域名托管您的 nodejs 服务器。

如果你想做一些不同于解析的事情,你可以发送一个拉取请求到parse-server . LiveQuery是贡献者创建的额外功能。

有关更多详细信息,请参阅来自 Parse.com 的链接, github wiki , 和 community links .

关于android - 将聊天服务器应用程序从 parse.com 移动到 google app engine,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37433212/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com