Browse Source

Telegram support and split of channel controllers into separate files. Thanks to @schurig

Martin Edenhofer 8 years ago
parent
commit
d34112a99f

+ 1 - 0
Gemfile

@@ -42,6 +42,7 @@ gem 'omniauth-linkedin-oauth2'
 gem 'omniauth-twitter'
 
 gem 'twitter'
+gem 'telegramAPI'
 gem 'koala'
 gem 'mail'
 gem 'email_verifier'

+ 8 - 0
Gemfile.lock

@@ -198,6 +198,7 @@ GEM
     nenv (0.3.0)
     nestful (1.1.1)
     net-ldap (0.15.0)
+    netrc (0.11.0)
     nokogiri (1.6.8)
       mini_portile2 (~> 2.1.0)
       pkg-config (~> 1.1.7)
@@ -293,6 +294,10 @@ GEM
     rb-inotify (0.9.7)
       ffi (>= 0.5.0)
     ref (2.0.0)
+    rest-client (2.0.0)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
     retriable (2.1.0)
     rspec-core (3.5.4)
       rspec-support (~> 3.5.0)
@@ -358,6 +363,8 @@ GEM
       activesupport (>= 4.0)
       sprockets (>= 3.0.0)
     sqlite3 (1.3.11)
+    telegramAPI (1.2.2)
+      rest-client (~> 2.0, >= 1.7.3)
     term-ansicolor (1.3.2)
       tins (~> 1.0)
     test-unit (3.2.1)
@@ -467,6 +474,7 @@ DEPENDENCIES
   spring-commands-rspec
   sprockets
   sqlite3
+  telegramAPI
   test-unit
   therubyracer
   twitter

+ 51 - 36
app/assets/javascripts/app/controllers/_channel/email.coffee

@@ -243,7 +243,7 @@ class App.ChannelEmailAccountOverview extends App.Controller
     @ajax(
       id:   'email_index'
       type: 'GET'
-      url:  @apiPath + '/channels/email_index'
+      url:  "#{@apiPath}/channels_email"
       processData: true
       success: (data, status, xhr) =>
         @stopLoading()
@@ -324,34 +324,44 @@ class App.ChannelEmailAccountOverview extends App.Controller
   delete: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    new App.ControllerGenericDestroyConfirm(
-      item:      item
+    new App.ControllerConfirm(
+      message: 'Sure?'
+      callback: =>
+        @ajax(
+          id:   'email_delete'
+          type: 'DELETE'
+          url:  "#{@apiPath}/channels_email"
+          data: JSON.stringify(id: id)
+          processData: true
+          success: =>
+            @load()
+        )
       container: @el.closest('.content')
-      callback:  @load
     )
 
   disable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = false
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'email_disable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_email_disable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
   enable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = true
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'email_enable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_email_enable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
@@ -441,7 +451,7 @@ class App.ChannelEmailEdit extends App.ControllerModal
     # show errors in form
     if errors
       @log 'error', errors
-      @formValidate( form: e.target, errors: errors )
+      @formValidate(form: e.target, errors: errors)
       return false
 
     # disable form
@@ -449,16 +459,18 @@ class App.ChannelEmailEdit extends App.ControllerModal
 
     # update
     @ajax(
-      id:   'channel_group_update'
+      id:   'channel_email_group'
       type: 'POST'
-      url:  "#{@apiPath}/channels/group/#{@item.id}"
-      data: JSON.stringify( params )
+      url:  "#{@apiPath}/channels_email_group/#{@item.id}"
+      data: JSON.stringify(params)
       processData: true
       success: (data, status, xhr) =>
         @callback()
         @close()
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @formEnable(e)
+        @el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
     )
 
 class App.ChannelEmailAccountWizard extends App.WizardModal
@@ -651,7 +663,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
     @ajax(
       id:   'email_probe'
       type: 'POST'
-      url:  @apiPath + '/channels/email_probe'
+      url:  "#{@apiPath}/channels_email_probe"
       data: JSON.stringify(params)
       processData: true
       success: (data, status, xhr) =>
@@ -683,7 +695,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
           @$('.js-inbound [name="options::password"]').val(@account['meta']['password'])
 
         @enable(e)
-      fail: =>
+      error: =>
         @enable(e)
         @showSlide('js-intro')
     )
@@ -705,7 +717,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
     @ajax(
       id:   'email_inbound'
       type: 'POST'
-      url:  @apiPath + '/channels/email_inbound'
+      url:  "#{@apiPath}/channels_email_inbound"
       data: JSON.stringify(params)
       processData: true
       success: (data, status, xhr) =>
@@ -738,9 +750,10 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
           @showAlert('js-inbound', data.message_human || data.message)
           @showInvalidField('js-inbound', data.invalid_field)
         @enable(e)
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @showSlide('js-inbound')
-        @showAlert('js-inbound', data.message_human || data.message)
+        @showAlert('js-inbound', data.message_human || data.message || data.error)
         @showInvalidField('js-inbound', data.invalid_field)
         @enable(e)
     )
@@ -768,8 +781,8 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
     @ajax(
       id:   'email_outbound'
       type: 'POST'
-      url:  @apiPath + '/channels/email_outbound'
-      data: JSON.stringify( params )
+      url:  "#{@apiPath}/channels_email_outbound"
+      data: JSON.stringify(params)
       processData: true
       success: (data, status, xhr) =>
         if data.result is 'ok'
@@ -783,9 +796,10 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
           @showAlert('js-outbound', data.message_human || data.message)
           @showInvalidField('js-outbound', data.invalid_field)
         @enable(e)
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @showSlide('js-outbound')
-        @showAlert('js-outbound', data.message_human || data.message)
+        @showAlert('js-outbound', data.message_human || data.message || data.error)
         @showInvalidField('js-outbound', data.invalid_field)
         @enable(e)
     )
@@ -810,7 +824,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
     @ajax(
       id:   'email_verify'
       type: 'POST'
-      url:  @apiPath + '/channels/email_verify'
+      url:  "#{@apiPath}/channels_email_verify"
       data: JSON.stringify(account)
       processData: true
       success: (data, status, xhr) =>
@@ -835,7 +849,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal
               if data.subject && @account
                 @account.subject = data.subject
               @verify(@account, count + 1)
-      fail: =>
+      error: =>
         @showSlide('js-intro')
         @showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.')
     )
@@ -946,7 +960,7 @@ class App.ChannelEmailNotificationWizard extends App.WizardModal
     @ajax(
       id:   'email_outbound'
       type: 'POST'
-      url:  "#{@apiPath}/channels/email_notification"
+      url:  "#{@apiPath}/channels_email_notification"
       data: JSON.stringify(params)
       processData: true
       success: (data, status, xhr) =>
@@ -957,9 +971,10 @@ class App.ChannelEmailNotificationWizard extends App.WizardModal
           @showAlert('js-outbound', data.message_human || data.message)
           @showInvalidField('js-outbound', data.invalid_field)
         @enable(e)
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @showSlide('js-outbound')
-        @showAlert('js-outbound', data.message_human || data.message)
+        @showAlert('js-outbound', data.message_human || data.message || data.error)
         @showInvalidField('js-outbound', data.invalid_field)
         @enable(e)
     )

+ 32 - 29
app/assets/javascripts/app/controllers/_channel/facebook.coffee

@@ -20,7 +20,7 @@ class Index extends App.ControllerSubContent
     @ajax(
       id:   'facebook_index'
       type: 'GET'
-      url:  "#{@apiPath}/channels/facebook_index"
+      url:  "#{@apiPath}/channels_facebook"
       processData: true
       success: (data, status, xhr) =>
         @stopLoading()
@@ -62,9 +62,6 @@ class Index extends App.ControllerSubContent
     @html App.view('facebook/list')(
       channels: channels
     )
-      # accounts: accounts
-      # showDescription: showDescription
-      # description:     description
 
     if @channel_id
       @edit(undefined, @channel_id)
@@ -103,43 +100,47 @@ class Index extends App.ControllerSubContent
   delete: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    new App.ControllerGenericDestroyConfirm(
-      item:      item
+    new App.ControllerConfirm(
+      message: 'Sure?'
+      callback: =>
+        @ajax(
+          id:   'facebook_delete'
+          type: 'DELETE'
+          url:  "#{@apiPath}/channels_facebook"
+          data: JSON.stringify(id: id)
+          processData: true
+          success: =>
+            @load()
+        )
       container: @el.closest('.content')
-      callback:  @load
     )
 
   disable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = false
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'facebook_disable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_facebook_disable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
   enable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = true
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'facebook_enable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_facebook_enable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
-  description: (e) =>
-    new App.ControllerGenericDescription(
-      description: App.Twitter.description
-      container:   @el.closest('.content')
-    )
-
 class AppConfig extends App.ControllerModal
   head: 'Connect Facebook App'
   shown: true
@@ -182,7 +183,7 @@ class AppConfig extends App.ControllerModal
             done: =>
               @isChanged = true
               @close()
-            fail: ->
+            fail: =>
               @el.find('.alert').removeClass('hidden').text('Unable to create entry.')
           )
           return
@@ -241,14 +242,16 @@ class AccountEdit extends App.ControllerModal
     @ajax(
       id:   'channel_facebook_update'
       type: 'POST'
-      url:  "#{@apiPath}/channels/facebook_verify/#{@channel.id}"
+      url:  "#{@apiPath}/channels_facebook/#{@channel.id}"
       data: JSON.stringify(@channel.attributes())
       processData: true
       success: (data, status, xhr) =>
         @isChanged = true
         @close()
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @formEnable(e)
+        @el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
     )
 
 App.Config.set('Facebook', { prio: 5100, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: Index, permission: ['admin.channel_facebook'] }, 'NavBarAdmin')

+ 202 - 0
app/assets/javascripts/app/controllers/_channel/telegram.coffee

@@ -0,0 +1,202 @@
+class Index extends App.ControllerSubContent
+  requiredPermission: 'admin.channel_telegram'
+  events:
+    'click .js-new':       'new'
+    'click .js-edit':      'edit'
+    'click .js-delete':    'delete'
+    'click .js-disable':   'disable'
+    'click .js-enable':    'enable'
+
+  constructor: ->
+    super
+
+    #@interval(@load, 60000)
+    @load()
+
+  load: =>
+    @startLoading()
+    @ajax(
+      id:   'telegram_index'
+      type: 'GET'
+      url:  "#{@apiPath}/channels_telegram"
+      processData: true
+      success: (data) =>
+        @stopLoading()
+        App.Collection.loadAssets(data.assets)
+        @render(data)
+    )
+
+  render: (data) =>
+
+    channels = []
+    for channel_id in data.channel_ids
+      channel = App.Channel.find(channel_id)
+      if channel && channel.options
+        displayName = '-'
+        if channel.group_id
+          group = App.Group.find(channel.group_id)
+          displayName = group.displayName()
+        channel.options.groupName = displayName
+      channels.push channel
+    @html App.view('telegram/index')(
+      channels: channels
+    )
+
+  new: (e) =>
+    e.preventDefault()
+    new BotAdd(
+      container: @el.parents('.content')
+      load: @load
+    )
+
+  edit: (e) =>
+    e.preventDefault()
+    id = $(e.target).closest('.action').data('id')
+    channel = App.Channel.find(id)
+    new BotEdit(
+      channel: channel
+      container: @el.parents('.content')
+      load: @load
+    )
+
+  delete: (e) =>
+    e.preventDefault()
+    id   = $(e.target).closest('.action').data('id')
+    new App.ControllerConfirm(
+      message: 'Sure?'
+      callback: =>
+        @ajax(
+          id:   'telegram_delete'
+          type: 'DELETE'
+          url:  "#{@apiPath}/channels_telegram"
+          data: JSON.stringify(id: id)
+          processData: true
+          success: =>
+            @load()
+        )
+      container: @el.closest('.content')
+    )
+
+  disable: (e) =>
+    e.preventDefault()
+    id   = $(e.target).closest('.action').data('id')
+    @ajax(
+      id:   'telegram_disable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_telegram_disable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
+        @load()
+    )
+
+  enable: (e) =>
+    e.preventDefault()
+    id   = $(e.target).closest('.action').data('id')
+    @ajax(
+      id:   'telegram_enable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_telegram_enable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
+        @load()
+    )
+
+class BotAdd extends App.ControllerModal
+  head: 'Add Telegram Bot'
+  shown: true
+  button: 'Add'
+  buttonCancel: true
+  small: true
+
+  content: ->
+    content = $(App.view('telegram/bot_add')())
+    createGroupSelection = (selected_id) ->
+      return App.UiElement.select.render(
+        name: 'group_id'
+        multiple: false
+        limit: 100
+        null: false
+        relation: 'Group'
+        nulloption: true
+        value: selected_id
+        class: 'form-control--small'
+      )
+
+    content.find('.js-select').on('click', (e) =>
+      @selectAll(e)
+    )
+    content.find('.js-messagesGroup').replaceWith createGroupSelection(1)
+    content
+
+  onClosed: =>
+    return if !@isChanged
+    @isChanged = false
+    @load()
+
+  onSubmit: (e) =>
+    @formDisable(e)
+    @ajax(
+      id:   'telegram_app_verify'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_telegram"
+      data: JSON.stringify(@formParams())
+      processData: true
+      success: =>
+        @isChanged = true
+        @close()
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
+        @formEnable(e)
+        @el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save Bot.')
+    )
+
+class BotEdit extends App.ControllerModal
+  head: 'Telegram Account'
+  shown: true
+  buttonCancel: true
+
+  content: ->
+    content = $(App.view('telegram/bot_edit')(channel: @channel))
+
+    createGroupSelection = (selected_id) ->
+      return App.UiElement.select.render(
+        name: 'group_id'
+        multiple: false
+        limit: 100
+        null: false
+        relation: 'Group'
+        nulloption: true
+        value: selected_id
+        class: 'form-control--small'
+      )
+
+    content.find('.js-messagesGroup').replaceWith createGroupSelection(@channel.group_id)
+    content
+
+  onClosed: =>
+    return if !@isChanged
+    @isChanged = false
+    @load()
+
+  onSubmit: (e) =>
+    @formDisable(e)
+    params = @formParams()
+    @channel.options = params
+    @ajax(
+      id:   'channel_telegram_update'
+      type: 'PUT'
+      url:  "#{@apiPath}/channels_telegram/#{@channel.id}"
+      data: JSON.stringify(@formParams())
+      processData: true
+      success: =>
+        @isChanged = true
+        @close()
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
+        @formEnable(e)
+        @el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
+    )
+
+App.Config.set('Telegram', { prio: 5100, name: 'Telegram', parent: '#channels', target: '#channels/telegram', controller: Index, permission: ['admin.channel_telegram'] }, 'NavBarAdmin')

+ 31 - 28
app/assets/javascripts/app/controllers/_channel/twitter.coffee

@@ -19,7 +19,7 @@ class Index extends App.ControllerSubContent
     @ajax(
       id:   'twitter_index'
       type: 'GET'
-      url:  "#{@apiPath}/channels/twitter_index"
+      url:  "#{@apiPath}/channels_twitter"
       processData: true
       success: (data, status, xhr) =>
         @stopLoading()
@@ -61,9 +61,6 @@ class Index extends App.ControllerSubContent
     @html App.view('twitter/list')(
       channels: channels
     )
-      # accounts: accounts
-      # showDescription: showDescription
-      # description:     description
 
     if @channel_id
       @edit(undefined, @channel_id)
@@ -102,43 +99,47 @@ class Index extends App.ControllerSubContent
   delete: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    new App.ControllerGenericDestroyConfirm(
-      item:      item
+    new App.ControllerConfirm(
+      message: 'Sure?'
+      callback: =>
+        @ajax(
+          id:   'twitter_delete'
+          type: 'DELETE'
+          url:  "#{@apiPath}/channels_twitter"
+          data: JSON.stringify(id: id)
+          processData: true
+          success: =>
+            @load()
+        )
       container: @el.closest('.content')
-      callback:  @load
     )
 
   disable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = false
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'twitter_disable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_twitter_disable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
   enable: (e) =>
     e.preventDefault()
     id   = $(e.target).closest('.action').data('id')
-    item = App.Channel.find(id)
-    item.active = true
-    item.save(
-      done: =>
-        @load()
-      fail: =>
+    @ajax(
+      id:   'twitter_enable'
+      type: 'POST'
+      url:  "#{@apiPath}/channels_twitter_enable"
+      data: JSON.stringify(id: id)
+      processData: true
+      success: =>
         @load()
     )
 
-  description: (e) =>
-    new App.ControllerGenericDescription(
-      description: App.Twitter.description
-      container:   @el.closest('.content')
-    )
-
 class AppConfig extends App.ControllerModal
   head: 'Connect Twitter App'
   shown: true
@@ -277,14 +278,16 @@ class AccountEdit extends App.ControllerModal
     @ajax(
       id:   'channel_twitter_update'
       type: 'POST'
-      url:  "#{@apiPath}/channels/twitter_verify/#{@channel.id}"
+      url:  "#{@apiPath}/channels_twitter/#{@channel.id}"
       data: JSON.stringify(@channel.attributes())
       processData: true
       success: (data, status, xhr) =>
         @isChanged = true
         @close()
-      fail: =>
+      error: (xhr) =>
+        data = JSON.parse(xhr.responseText)
         @formEnable(e)
+        @el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
     )
 
 App.Config.set('Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: Index, permission: ['admin.channel_twitter'] }, 'NavBarAdmin')

+ 44 - 8
app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee

@@ -1,13 +1,14 @@
 class App.TicketZoomArticleActions extends App.Controller
   events:
-    'click [data-type=public]':                    'publicInternal'
-    'click [data-type=internal]':                  'publicInternal'
-    'click [data-type=emailReply]':                'emailReply'
-    'click [data-type=emailReplyAll]':             'emailReplyAll'
-    'click [data-type=twitterStatusReply]':        'twitterStatusReply'
-    'click [data-type=twitterDirectMessageReply]': 'twitterDirectMessageReply'
-    'click [data-type=facebookFeedReply]':         'facebookFeedReply'
-    'click [data-type=delete]':                    'delete'
+    'click [data-type=public]':                       'publicInternal'
+    'click [data-type=internal]':                     'publicInternal'
+    'click [data-type=emailReply]':                   'emailReply'
+    'click [data-type=emailReplyAll]':                'emailReplyAll'
+    'click [data-type=twitterStatusReply]':           'twitterStatusReply'
+    'click [data-type=twitterDirectMessageReply]':    'twitterDirectMessageReply'
+    'click [data-type=facebookFeedReply]':            'facebookFeedReply'
+    'click [data-type=telegramPersonalMessageReply]': 'telegramPersonalMessageReply'
+    'click [data-type=delete]':                       'delete'
 
   constructor: ->
     super
@@ -151,6 +152,13 @@ class App.TicketZoomArticleActions extends App.Controller
         icon: 'reply'
         href: '#'
       }
+    if article.sender.name is 'Customer' && article.type.name is 'telegram personal-message'
+      actions.push {
+        name: 'reply'
+        type: 'telegramPersonalMessageReply'
+        icon: 'reply'
+        href: '#'
+      }
 
     actions.push {
       name: 'split'
@@ -399,6 +407,34 @@ class App.TicketZoomArticleActions extends App.Controller
 
     App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew } )
 
+  telegramPersonalMessageReply: (e) =>
+    e.preventDefault()
+
+    # get reference article
+    article_id = $(e.target).parents('[data-id]').data('id')
+    article    = App.TicketArticle.fullLocal(article_id)
+    sender     = App.TicketArticleSender.find(article.sender_id)
+    type       = App.TicketArticleType.find(article.type_id)
+    customer   = App.User.find(article.created_by_id)
+
+    @scrollToCompose()
+
+    # empty form
+    articleNew = {
+      to:          ''
+      cc:          ''
+      body:        ''
+      in_reply_to: ''
+    }
+
+    if article.message_id
+      articleNew.in_reply_to = article.message_id
+
+    # get current body
+    articleNew.body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html().trim() || ''
+
+    App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew, position: 'end' } )
+
   delete: (e) =>
     e.preventDefault()
 

+ 18 - 1
app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee

@@ -42,6 +42,8 @@ class App.TicketZoomArticleNew extends App.Controller
         possibleArticleType['email'] = true
       else if articleTypeCreate is 'facebook feed post'
         possibleArticleType['facebook feed comment'] = true
+      else if articleTypeCreate is 'telegram personal-message'
+        possibleArticleType['telegram personal-message'] = true
     if @ticket && @ticket.customer_id
       customer = App.User.find(@ticket.customer_id)
       if customer.email
@@ -105,6 +107,16 @@ class App.TicketZoomArticleNew extends App.Controller
         internal:   false,
         features:   ['attachment']
       }
+    if possibleArticleType['telegram personal-message']
+      @articleTypes.push {
+        name:              'telegram personal-message'
+        icon:              'telegram'
+        attributes:        []
+        internal:          false,
+        features:          ['attachment']
+        maxTextLength:     10000
+        warningTextLength: 5000
+      }
 
     if @permissionCheck('ticket.customer')
       @type = 'note'
@@ -335,6 +347,11 @@ class App.TicketZoomArticleNew extends App.Controller
       params.content_type = 'text/plain'
       params.body = App.Utils.html2text(params.body, true)
 
+    if params.type is 'telegram personal-message'
+      App.Utils.htmlRemoveRichtext(@$('[data-name=body]'), false)
+      params.content_type = 'text/plain'
+      params.body = App.Utils.html2text(params.body, true)
+
     params
 
   validate: =>
@@ -499,7 +516,7 @@ class App.TicketZoomArticleNew extends App.Controller
       @$('[data-name=body] [data-signature=true]').remove()
 
     # remove richtext
-    if @type is 'twitter status' || @type is 'twitter direct-message'
+    if @type is 'twitter status' || @type is 'twitter direct-message' || @type is 'telegram personal-message'
       rawHTML = @$('[data-name=body]').html()
       cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
       if cleanHTML && cleanHTML.html() != rawHTML

+ 32 - 0
app/assets/javascripts/app/views/telegram/bot_add.jst.eco

@@ -0,0 +1,32 @@
+<div class="alert alert--danger hidden" role="alert"></div>
+<p>
+  <%- @T('The tutorial on how to manage a %s is hosted on our [online documentation](https://zammad.org/documentation/channel/telegram).', 'Telegram Bot') %>
+</p>
+<fieldset>
+  <h2><%- @T('Enter your %s App Keys', 'Telegram') %></h2>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for="api_token"><%- @T('%s Api Token', 'Telegram') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <input id="api_token" type="text" name="api_token" value="" class="form-control" required autocomplete="off">
+    </div>
+  </div>
+  <h2><%- @T('Settings') %></h2>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for="welcome"><%- @T('Welcome message') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <input id="welcome" type="text" name="welcome" value="" placeholder="<%- @Ti('You are welcome! Just ask me something!') %>" class="form-control" required autocomplete="off">
+    </div>
+  </div>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for=""><%- @T('Choose which group %s will get added to.', 'messages') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <div class="js-messagesGroup"></div>
+    </div>
+  </div>
+</fieldset>

+ 29 - 0
app/assets/javascripts/app/views/telegram/bot_edit.jst.eco

@@ -0,0 +1,29 @@
+<div class="alert alert--danger hidden" role="alert"></div>
+<fieldset>
+  <h2><%- @T('Enter your %s App Keys', 'Telegram') %></h2>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for="api_token"><%- @T('%s Api Token', 'Telegram') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <input id="api_token" type="text" name="api_token" value="<%= @channel.options.api_token %>" class="form-control" required autocomplete="off">
+    </div>
+  </div>
+  <h2><%- @T('Settings') %></h2>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for="welcome"><%- @T('welcome message') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <input id="welcome" type="text" name="welcome" value="<%= @channel.options.welcome %>" placeholder="<%- @Ti('You are welcome! Just ask me something!') %>" class="form-control" required autocomplete="off">
+    </div>
+  </div>
+  <div class="input form-group">
+    <div class="formGroup-label">
+      <label for=""><%- @T('Choose which group %s will get added to.', 'messages') %> <span>*</span></label>
+    </div>
+    <div class="controls">
+      <div class="js-messagesGroup"></div>
+    </div>
+  </div>
+</fieldset>

Some files were not shown because too many files changed in this diff