Project

General

Profile

« Previous | Next » 

Revision 688a21d7

Added by Marc Dequènes almost 11 years ago

  • ID 688a21d76fad2957379272f7d454ec283f8a4a33

[evol] conversation/bot protocol rework §3 (refs #30)

View differences:

lib/cyborghood/cyborg/conversation.rb
27 27
  module BotProtocol
28 28
    VERSION = "0.1"
29 29

  
30
    module_function
30
    # TODO:
31
    #   - check for request/reply couples (reply to wrong of non-existent request)
32
    #   - check for negociation wip/done
31 33

  
32
    def process_received_message(message)
34
    def initialize(conversation)
35
      @conversation = conversation
36

  
37
      @negociation_received = false
38
      @negociation_sent = false
39
      @negociation_ok = false
40
    end
41

  
42
    def negociation_ok?
43
      @negociation_ok
33 44
    end
34 45

  
35
    def receive(action, parameters)
36
      method = "receive_" + action.downcase.tr(" ", "_")
46
    def process_received_message(message)
47
      method = "receive_" + message.action_code.downcase.tr(" ", "_")
37 48
      if respond_to? method
38
        send(method, parameters)
49
        send(method, message)
39 50
      else
40 51
        send_error_protocol("unknown action")
41 52
      end
42 53
    end
43 54

  
44
    def receive_announce_helo(parameters)
45
      unless parameters[:bot_name] =~ Conversation::BOT_ID_PATTERN
46
        return send_error_protocol "bad bot name"
55
    def receive_announce_helo(message)
56
      unless message.conv_thread.id == 0
57
        return send_quit_decline "bad negociation"
47 58
      end
48
      unless parameters[:protocol_version] == VERSION
49
        # TODO: send QUIT DECLINE "protocol version does not match"
50
        return
59
      unless message.parameters[:bot_name] =~ Conversation::BOT_ID_PATTERN
60
        return send_quit_decline "bad bot name"
51 61
      end
52
      @conversation.set_peer_info(parameters[:bot_name], parameters[:capabilities])
53
      send_reply_ok
62
      unless message.parameters[:protocol_version] == VERSION
63
        return send_quit_decline "protocol version does not match"
64
      end
65
      @negociation_received = true
66
      @conversation.set_peer_info(message.parameters[:bot_name], message.parameters[:capabilities])
67

  
68
      if @negociation_sent
69
        send_announce_ok(message)
70
        @negociation_ok = true
71
      else
72
        send_announce_helo(message)
73
        @negociation_sent = true
74
      end
75
    end
76

  
77
    def receive_announce_ok(message)
78
      unless @negociation_sent and @negociation_received
79
        send_quit_decline "bad negociation"
80
      end
81
      @negociation_ok = true
82
    end
83

  
84
    def send_announce_helo(recv_message = nil)
85
      action_code = "ANNOUNCE HELO"
86
      message = (recv_message.nil? ? @conversation.thread('system').new_message(action_code) :
87
                 recv_message.create_reply(action_code))
88
      message.send
54 89
    end
55 90

  
56
    def send_error_protocol(msg = nil)
57
      @conversation.set_error_status
58
      @conversation.send_peer("ERROR PROTO", { :error => msg || "" })
91
    def send_announce_ok(recv_message)
92
      recv_message.create_reply("ANNOUNCE OK").send
59 93
    end
60 94

  
61
    def send_reply_ack
62
      @conversation.send_peer("REPLY ACK")
95
    def send_error_protocol(message = nil)
96
      # message can be nil if nothing could be parsed (or we may close the conversation, dunno)
97
      # TODO
98
      # @conversation.set_error_status
99
      # @conversation.send_peer("ERROR PROTO", { :error => msg || "" })
63 100
    end
64 101

  
65
    def send_quit(reason = nil)
66
      action = "QUIT " + (reason.nil? ? "LEAVING" : "DECLINE")
67
      parameters = (reason.nil? ? nil : { :reason => reason })
68
      @conversation.send_peer(action, parameters)
102
    def send_reply_ack(recv_message)
103
      recv_message.create_reply("REPLY ACK").send
104
    end
105

  
106
    def send_quit_decline(recv_message, reason)
107
      recv_message.create_reply("QUIT LEAVING", { :reason => reason }).send
108
    end
109

  
110
    def send_quit_leaving
111
      conversation.thread('system').new_message("QUIT LEAVING").send
69 112
    end
70 113
  end
71 114

  
72 115
  class ConversationThread
73 116
    attr_reader :conversation, :name, :id, :session
74 117

  
75
    @@next_id = 0
76

  
77
    def initialized(conversation, name)
118
    def initialized(conversation, id, name)
78 119
      @conversation = conversation
79 120
      @name = name
121
      @id = id
80 122

  
81
      @id = @@next_id
82
      @@next_id += 1
83 123
      @session = Session.new
84 124
      @next_action_id = 0
85 125
    end
86 126

  
87 127
    def new_message(action_code, parameters = nil, action_id = nil)
88
      Message.new(this, action_code, parameters)
128
      Message.new(self, action_code, parameters)
89 129
    end
90 130

  
91 131
    def next_action_id
......
119 159
    def send
120 160
      raise CyberError.new(:unrecoverable, "bot/conversation", "Not sending twice the same message") unless self.new?
121 161
      @action_id = @conv_thread.next_action_id
122
      @conv_thread.conversation.send_message(this)
162
      @conv_thread.conversation.send_message(self)
123 163
    end
124 164

  
125 165
    def create_reply(action_code, parameters)
126 166
      raise CyberError.new(:unrecoverable, "bot/conversation", "Cannot reply to a newly created message") if self.new?
127
      new(this, action_code, parameters)
167
      new(self, action_code, parameters)
128 168
    end
129 169
  end
130 170

  
......
158 198
      # associated conversation threads
159 199
      @conv_threads = {}
160 200
      # thread 0 is reserved
201
      @next_thread_id = 0
161 202
      @system_thread = self.thread('system')
162 203

  
163 204
      # post-negociation peer info
164 205
      @peer_id = nil
165 206
      @peer_capabilities = []
207

  
208
      @protocol = BotProtocol.new(self)
166 209
    end
167 210

  
168 211
    def clear_receive_info
......
232 275
    def receive_message(message)
233 276
      logger.debug "Received message '#{action}' [#{identifier}]"
234 277

  
235
      BotProtocol.process_received_message(message)
278
      @protocol.process_received_message(message)
236 279
    end
237 280

  
238 281
    def receive_error(msg)
......
256 299
    end
257 300

  
258 301
    def thread(name = 'default')
259
      th = @conv_threads[name] || ConversationThread.new(this, name)
260
      # allow searching by id too
261
      @conv_threads_index[th.id] = name
262
      th
302
      @conv_threads[name] || new_thread(name)
263 303
    end
264 304

  
265 305
    def thread_by_id(id)
266 306
      name = @conv_threads_index[id]
267
      name.nil? ? ConversationThread.new(this, "noname/#{index}") : @conv_threads[name]
307
      name.nil? ? new_thread("noname/#{id}", id) : @conv_threads[name]
268 308
    end
269 309

  
270 310
    def send_message(message)
......
281 321

  
282 322
    protected
283 323

  
324
    def new_thread(name, id = nil)
325
      id ||= @next_thread_id
326
      th = ConversationThread.new(self, id, name)
327
      @next_thread_id = [@next_thread_id, id + 1].max
328

  
329
      @conv_threads[th.name] = th
330
      # allow searching by id too
331
      @conv_threads_index[th.id] = name
332
      th
333
    end
334

  
284 335
    def reply_syntax_error(msg = nil)
285 336
      logger.error "Protocol error [#{identifier}]: syntax error (#{msg})"
286 337
      set_error_status

Also available in: Unified diff