- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在看WWDC 2014“与Xcode的持续集成”视频,看起来如何使用机器人进行测试看起来很棒。
但是我的问题是,当他向Jeeves发送消息“集成CoffeeBoard”时,看到该视频的任何人。Bot开始集成。我想知道他是怎么做到的。
我想在github上添加post-receive钩子,该钩子在接收到任何提交后应在OS X Server上启动Xcode bot。我的大多数团队成员都使用SourceTree或GitHub来管理他们的git,并且他们不想使用Xcode Source Control。我认为创建一个机器人并将其选项设置为手动启动就可以解决问题。我需要知道,“ OS X Server是否为您提供了诸如会启动bot的url之类的选项?”
抱歉,如果我不够清楚。但这对我来说太混乱了,因为他们关于触发器的文档很少。尽管他提到这是很酷的新功能,但他们没有提供任何信息来实现这一目标
最佳答案
前两个答案并不能完全回答最初的问题“他们是如何做到的”,以便从Messages应用程序中启动机器人。
我已经重新创建了模仿Jeeves虚拟助手与机器人进行交互(并获取天气)所需的确切工作流程和脚本。
有关完整的详细信息,请参见链接的PDF文档:
https://s3.amazonaws.com/icefield/IntegratingXcodeBotsWithMessages.pdf
编辑:原始答案被删除,我相信是由于我通过完整答案的链接引用了这一事实。此编辑将完整的实施详细信息添加为此答案的一部分。我希望不久就可以得到一个答案。
将Xcode Bot与消息集成
在WWDC 2014 Session 415(与Xcode 6持续集成)中,Apple演示了通过自定义集成触发器将Xcode bot与Messages应用程序集成。更具体地说,从该会话视频(https://developer.apple.com/videos/play/wwdc2014-415/)的23分钟开始,Apple演示了结合使用集成触发器和Messages来接收构建服务器上的集成状态。此外,通过使用虚拟聊天室成员Jeeves,他们展示了直接从Messages应用程序中开始集成的能力。下面的文章提供了重现该功能的分步说明。
客户端和服务器配置
首先,这是我用来模仿Jeeves功能的客户端和服务器的配置:
客户
OS X版本10.11(El Capitan),Xcode 7.0.1
服务器
OS X版本10.11(El Capitan),OS X Server 5.0.4,Xcode 7.0.1,Ruby 2.0.0p645
网络
为了进行开发和持续集成,我使用内部网络。我的OS X Server位于domain.local,而我的开发机器是同一内部网络上的另一个节点。无论您使用内部还是外部服务器,以下说明均应适用。
Jabber –消息的基础
Jabber是用于实例消息传递的开源协议的原始名称。 Jabber被重命名为可扩展消息和状态协议(XMPP)。 OS X Messages应用是使用Jabber为其核心构建的。
为此,我们将广泛使用Jabber(消息),因此请确保它已打开。在OS X Server App中,选择“服务”>“消息”视图,然后在右上角打开“消息”。对于Jeeves,我使用的Messages服务设置如下:
在服务器的终端窗口中,如果要检查Jabber的特定设置,请使用
$ sudo serveradmin settings jabber
$ gem install xmpp4r
Options +ExecCGI
<FilesMatch message_room$>
SetHandler cgi-script
</FilesMatch>
#!/usr/bin/env ruby
require 'json'
require 'net/http'
require 'uri'
# -------------------------------------------------------------------------------------
# credentials and such
domain = "<yourDomain>.local"
# -------------------------------------------------------------------------------------
# our messaging endpoint
uri = URI.parse("http://#{domain}:80/message_room")
# -------------------------------------------------------------------------------------
# what we want to say
message = "#{ENV['XCS_BOT_NAME']} integration #{ENV['XCS_INTEGRATION_NUMBER']} is now starting."
# -------------------------------------------------------------------------------------
# build up the request body
reqBody = {:message => message}
body = JSON.generate(reqBody)
# -------------------------------------------------------------------------------------
# the connect type
http = Net::HTTP.new(uri.host, uri.port)
# -------------------------------------------------------------------------------------
# build up the request
request = Net::HTTP::Post.new(uri.request_uri)
request.add_field('Content-type', 'application/json')
request.body = body
# -------------------------------------------------------------------------------------
# send the request and get the response
response = http.request(request)
#!/usr/bin/env ruby
require 'json'
require 'net/http'
require 'uri'
# -------------------------------------------------------------------------------------
# credentials and such
domain = "<yourDomain>.local"
# -------------------------------------------------------------------------------------
# our messaging endpoint
uri = URI.parse("http://#{domain}:80/message_room")
# -------------------------------------------------------------------------------------
# what we want to say
integrationResult = case ENV['XCS_INTEGRATION_RESULT']
when "succeeded"
"has completed successfully."
when "test-failures"
tc = ENV['XCS_TEST_FAILURE_COUNT'].to_i
"completed with #{tc} failing #{(tc ==1 ) ? 'test' : 'tests'}."
when "build-errors"
ec = ENV['XCS_ERROR_COUNT'].to_i
"failed with #{ec} build #{(ec == 1) ? 'error' : 'errors'}."
when "warnings"
wc = ENV['XCS_WARNING_COUNT'].to_i
"completed with #{wc} #{(wc == 1) ? 'warning' : 'warnings'}."
when "analyzer-warnings"
ic = ENV['XCS_ANALYZER_WARNING_COUNT'].to_i
"completed with #{ic} static analysis #{(ic == 1) ? 'issue' : 'issues'}."
when "trigger-error"
"failed running trigger script."
when "checkout-error"
"failed to checkout from source control."
else
"failed with unexpected errors."
end
message = "#{ENV['XCS_BOT_NAME']} integration #{ENV['XCS_INTEGRATION_NUMBER']} #{integrationResult}"
# -------------------------------------------------------------------------------------
# build up the request body
reqBody = {:message => message}
body = JSON.generate(reqBody)
# -------------------------------------------------------------------------------------
# the connect type
http = Net::HTTP.new(uri.host, uri.port)
# -------------------------------------------------------------------------------------
# build up the request
request = Net::HTTP::Post.new(uri.request_uri)
request.add_field('Content-type', 'application/json')
request.body = body
# -------------------------------------------------------------------------------------
# send the request and get the response
response = http.request(request)
#!/usr/bin/env ruby
require 'cgi'
require 'json'
require 'xmpp4r'
require 'xmpp4r/muc'
# -------------------------------------------------------------------------------------
# credentials and such
domain = "<domain>.local"
userId = "jeeves@#{domain}"
userPw = "<jeevesAccountPassword>"
roomName = "integration@rooms.#{domain}"
# -------------------------------------------------------------------------------------
# header sent back
cgi = CGI.new
puts cgi.header( "type" => "text/html", "status" => "OK")
# -------------------------------------------------------------------------------------
# get the message out of the json formatted text
keyValue = JSON.parse(cgi.params.keys.first)
key = "message"
value = keyValue[key] puts value
# -------------------------------------------------------------------------------------
# create the message to the iChat (jabber) room
fromJID = Jabber::JID.new(userId)
jabberClient = Jabber::Client.new(fromJID)
jabberClient.connect
jabberClient.auth(userPw)
jabberClient.send(Jabber::Presence.new.set_type(:available))
# -------------------------------------------------------------------------------------
# send the message to a chat room
roomID = roomName + "/" + jabberClient.jid.node
roomJID = Jabber::JID::new(roomID)
room = Jabber::MUC::MUCClient.new(jabberClient) room.join(roomJID)
roomMessage = Jabber::Message.new(roomJID, value) room.send(roomMessage)
$ ruby ./jeevesManager.rb
#!/usr/bin/env ruby
require 'xmpp4r'
require 'xmpp4r/muc'
require 'xmpp4r/delay'
require './jeevesWeather.rb'
require './jeevesIntegration.rb'
# -------------------------------------------------------------------------------------
# credentials and such
domain = "<domain>.local"
userId = "jeeves@#{domain}"
userPw = "<jeevesAccountPassword>"
roomName = "integration@rooms.#{domain}"
defaultWeatherZipCode = "95015"
# -------------------------------------------------------------------------------------
# create the client we'll use
fromJID = Jabber::JID.new(userId)
jabberClient = Jabber::Client.new(fromJID)
jabberClient.connect
jabberClient.auth(userPw)
jabberClient.send(Jabber::Presence.new.set_type(:available))
# -------------------------------------------------------------------------------------
# connect to the chatroom
roomID = roomName + "/" + jabberClient.jid.node
roomJID = Jabber::JID::new(roomID)
room = Jabber::MUC::MUCClient.new(jabberClient) room.join(roomJID)
# -------------------------------------------------------------------------------------
# weather
def getWeather(m)
begin
words = m.body.downcase.split("weather")
where = defaultWeatherZipCode
if (words.length == 2)
where = words[1].strip
end
weather = get_weather_for_city(where,'f')
rescue
weather = "Couldn't get weather for that location - try zip code"
end
return weather
end
# -------------------------------------------------------------------------------------
# integration
def startIntegration(m)
begin
words = m.body.split("integrate")
botName = "Invalid BOT Name"
if (words.length == 2)
botName = words[1].strip
end
integrationMessage = jeevesIntegration(botName)
rescue
integrationMessage = "Failed integrating #{botName}"
end
return integrationMessage
end
# -------------------------------------------------------------------------------------
# listen for messages in chatroom (this callback will run in a separate thread)
room.add_message_callback do |m|
if (m.x.nil?) # the msg is current
if m.type != :error
body = m.body;
if (body.downcase.include? "jeeves")
# assume Jeeves does not understand command
understood = 0
# exit Jeeves
if (body.downcase.include? "exit")
understood = 1
message = "Good-bye"
mainthread.wakeup
end
# Weather
if (body.downcase.include? "weather")
understood = 1
message = getWeather(m)
end
# Integrate BOT
if (body.downcase.include? "integrate")
understood = 1
message = startIntegration(m)
end
# Jeeves doesn't understand command
if (understood == 0)
message = "I don't understand that command!"
end
# let user know what has happened
roomMessage = Jabber::Message.new(roomJID, message)
room.send(roomMessage)
end
end
end
end
# -------------------------------------------------------------------------------------
# add the callback to respond to server ping (to keep the connect alive)
jabberClient.add_iq_callback do |iq_received|
if iq_received.type == :get
if iq_received.queryns.to_s != 'http://jabber.org/protocol/disco#info'
iq = Jabber::Iq.new(:result, jabberClient.jid.node)
iq.id = iq_received.id
iq.from = iq_received.to
iq.to = iq_received.from
jabberClient.send(iq)
end
end
end
# -------------------------------------------------------------------------------------
# stop the main thread (the call back will still be alive this way)
print "Connected to chat room...\n"
Thread.stop
print "Disconnected from chat room...\n"
# leave chat room and log out of Jabber
room.exit
jabberClient.close
######### Weather #########
require 'rexml/document'
require 'open-uri'
require 'net/smtp'
# -------------------------------------------------------------------------------------
# yahoo weather url info
# http://developer.yahoo.net/weather/#examples
# -------------------------------------------------------------------------------------
#Returns a hash containing the location and temperature information
#Accepts US zip codes or Yahoo location id's
def yahoo_weather_query(loc_id, units)
h = {}
open("http://xml.weather.yahoo.com/forecastrss?p=#{loc_id}&u=#{units}") do |http|
response = http.read
doc = REXML::Document.new(response)
root = doc.root
channel = root.elements['channel']
location = channel.elements['yweather:location']
h[:city] = location.attributes["city"]
h[:region] = location.attributes["region"]
h[:country] = location.attributes["country"]
h[:temp] = channel.elements["item"].elements["yweather:condition"].attributes["temp"]
h[:text] = channel.elements["item"].elements["yweather:condition"].attributes["text"]
h[:wind_speed] = channel.elements['yweather:wind'].attributes['speed']
h[:humidity] = channel.elements['yweather:atmosphere'].attributes['humidity']
h[:sunrise] = channel.elements['yweather:astronomy'].attributes['sunrise']
h[:sunset] = channel.elements['yweather:astronomy'].attributes['sunset']
h[:forecast_low] = channel.elements["item"].elements['yweather:forecast'].attributes['low']
h[:forecast_high] = channel.elements["item"].elements['yweather:forecast'].attributes['high'] end
return h
end
# -------------------------------------------------------------------------------------
def get_weather_for_city(city_code,units)
weather_info = yahoo_weather_query(city_code, units)
city = weather_info[:city]
region = weather_info[:region]
country = weather_info[:country]
temp = weather_info[:temp]
wind_speed = weather_info[:wind_speed]
humidity = weather_info[:humidity]
text = weather_info[:text]
sunrise = weather_info[:sunrise]
sunset = weather_info[:sunset]
forecast_low = weather_info[:forecast_low]
forecast_high = weather_info[:forecast_high]
return "#{city}, #{region}:\n" + " Currently #{temp} degrees, #{humidity}% humidity, #{wind_speed} mph winds, #{text}.\n" + " Forecast: #{forecast_low} low, #{forecast_high} high.\n" + " Sunrise: #{sunrise}, sunset: #{sunset}.\n"
end
require 'json'
require 'open-uri'
require 'openssl'
# -------------------------------------------------------------------------------------
def jeevesIntegration(botToIntegrate)
# credentials
domain = "<domain>.local"
endpoint = "https://#{domain}:20343"
user = "your-integration-username (not Jeeves)"
password = "password"
# return message
message = "Bot '#{botToIntegrate}' does not exist on server #{domain}"
# request JSON construct with all the BOTS
botsRequestURI = URI.parse("#{endpoint}/api/bots")
output = open(botsRequestURI, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE})
bots = JSON.parse(output.readlines.join(""))
# loop through full list of BOTS for the one we're interested in
bots['results'].each do |bot|
botName = bot['name']
if (botName.downcase == botToIntegrate.downcase)
botID = bot['_id']
# curl -k -X POST -u "#{user}:#{password}" "#{endpoint}/api/bots/#{botid}/integrations" -i
# -------------------------------------------------------------------
# kickoff integration
uri = URI.parse(endpoint)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new("/api/bots/#{botID}/integrations")
request.basic_auth(user, password)
response = http.request(request)
message = "Integrating #{botName} on server #{domain}"
end
end
return message
end
关于xcode - 手动启动Xcode Bot集成吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28365091/
因此,我想创建一个 bot/incoming webhook 来读取群组中的所有消息并将其复制到电子表格中。我在这里要问的是,机器人或传入的 webhook 可以读取整个群组消息吗?在 google
我正在使用 Microsoft Bot Framework 开发一个机器人,在该机器人中,机器人将通过向用户发送一些图像来响应。我用 slack 和 skype 配置了它。 在 slack 中图像正在
我试图让我的机器人与用户开始对话,但我不知道应该从代码中的哪个位置发送消息。启动 convo 的文档在这里,但不是很有帮助:http://docs.botframework.com/connector
我正在创建 Telegram Bot 。我需要跟踪添加用户和添加用户的时间。我如何使用 Telegram API 来做到这一点? 最佳答案 在使用 Telegram API 的群组中,您可以调用 ne
我已经创建了几个 Telegram 机器人。它们适用于我的帐户,也适用于我测试过的其他几个帐户。 但我收到一些用户的报告,称机器人从不响应。 是否有一些用户设置会阻止帐户从机器人获取消息?或者任何其他
如果在一段时间内没有用户输入,我有一个关于取消提示或对话框的问题。 我需要一种方法来以某种方式在提示中超时。 Example: Bot prompts the user: "How old are y
我有一个用例,当我的机器人需要在一天中的特定时间向用户发送消息时。假设每天早上 6 点。 我正在使用预定的 azure Webjob 发送这些消息。消息将在 Slack 和 Skype 客户端中传递。
我是 Microsoft bot 的新手。我的公司有自己的通信应用程序,我想将我的机器人与通信应用程序连接起来,我的客户可以在我公司的通信应用程序上使用我的机器人。我读到它需要使用 Direct Li
我在 sdk V4 Bot 中实现了一个中间件来拦截 bot 和用户之间的每条消息并记录该自定义 mongo Db。我正在尝试为使用 SDK v4 构建的 Bot 实现类似的概念。看起来我可以使用以下
我对编程和学习还很陌生。我认为制作一个不和谐的机器人是一种很好的学习方式,我很享受,只是有点卡住了。所以我的机器人是私有(private)的,我们的不和谐服务器中有一个正在运行的笑话,每当用户发送“k
如何让机器人假装它正在输入消息? 当机器人假装输入时,聊天中会出现以下文本: 我使用 python aiogram框架,但对 native Telegram API 的建议也会有所帮助。 最佳答案 我
我有一个像这样的 Telegram Bot : 通过 webhook 获取更新 语言:C#(我也欢迎其他语言的回答) 我们有以下用户场景: 向机器人发送/MyPhoto a_parameter命令 向
我加入了一个 Telegram Bot ,但我不知道它的所有者。是否可以找到 Telegram Bot 的所有者? 最佳答案 根据 Telegram MTProto protocol 不可能看到 Te
我已经创建了一个电报机器人并按照文档中的描述设置了一个 webhook。出于测试目的,我已经设置了它,因此一旦您向机器人发送一条消息,它就会用相同的消息回复。 现在我遇到的问题是来自电报的更新非常缓慢
是否可以将位置从 Telegram 发送到 Bot,这是在 Bot Framework 中制作的? 我将我的位置从我的 Telegram 帐户发送到我的 Bot,但服务器没有收到它们(我没有收到回复)
我在 Telegram 上创建了一个组并创建了一个机器人并添加进来。 我正在尝试向我创建的组发送消息,错误如下所示 {"ok":false,"error_code":403,"description"
使用 Microsoft Bot Framework V3 我开始使用登录卡。 我从示例代码页做了一个简单的剪切并粘贴到我的代码中,假设它有效(编译): https://docs.botframewo
当用户刚刚打开聊天时,机器人如何向用户发送消息。 例子: 用户已经添加了 Telegram bot到他的联系人列表并开始对话 稍后,用户打开与该机器人的聊天窗口 机器人“看到”该用户已打开聊天窗口,但
将 XCode 更新到版本 11.4 (11E146) 后,我不再在机器人设置中看到存储库分支。但是我在 Source Controll Navigator 中看到了这些存储库分支。有谁知道我该如何解
我想将机器人的位置发送给用户,我在谷歌上搜索了很多,但我发现只有这种情况“将用户的位置发送给机器人”我想要相反的情况,意思是:“发送机器人的位置给用户”。这是我的想法:机器人的所有者是一个司机,他想与
我是一名优秀的程序员,十分优秀!