├── sv ├── env │ ├── TCC_LogLevel │ ├── TCC_OffHook │ ├── TCC_QrPort │ ├── TCC_Innate_Port │ ├── TCC_Monitors │ ├── TCC_Server │ ├── TCC_InternalProxy │ ├── TCC_QrAddress │ ├── TCC_Root │ ├── TCC_ProxyServerFormatString │ ├── TCC_WebSocketAgentURI │ └── TCC_WebSocketListenerURI ├── queue_router │ ├── log │ │ ├── main │ │ └── run │ ├── env │ └── run ├── fs2ws │ ├── run │ └── log │ │ └── run ├── tcc_innate │ ├── log │ │ └── run │ └── run └── pg2beanstalk │ ├── log │ └── run │ └── run ├── translation ├── en.yaml └── de.yaml ├── db └── PLACEHOLDER ├── public ├── images │ └── ear.png ├── js │ ├── serenade.min.js.gz │ ├── sort_table.js │ ├── tiers.js │ ├── sorter.js │ └── live2_views.js ├── bootstrap │ ├── bootstrap.zip │ ├── img │ │ ├── bird.png │ │ ├── browsers.png │ │ ├── github-16px.png │ │ ├── icon-css3.png │ │ ├── icon-github.png │ │ ├── icon-html5.png │ │ ├── less-small.png │ │ ├── icon-twitter.png │ │ ├── grid-18px-masked.png │ │ ├── less-logo-large.png │ │ ├── example-diagram-01.png │ │ ├── example-diagram-02.png │ │ ├── example-diagram-03.png │ │ ├── example-sites │ │ │ ├── bartop.png │ │ │ ├── fleetio.png │ │ │ ├── jshint.png │ │ │ ├── kippt.png │ │ │ ├── railwayjs.png │ │ │ └── totalwireframe.png │ │ ├── glyphicons-halflings.png │ │ ├── bootstrap-mdo-sfmoma-01.jpg │ │ ├── bootstrap-mdo-sfmoma-02.jpg │ │ ├── bootstrap-mdo-sfmoma-03.jpg │ │ ├── glyphicons-halflings-white.png │ │ ├── responsive-illustrations.png │ │ ├── examples │ │ │ ├── bootstrap-example-hero.jpg │ │ │ ├── bootstrap-example-fluid.jpg │ │ │ └── bootstrap-example-starter.jpg │ │ └── glyphicons │ │ │ ├── glyphicons_009_magic.png │ │ │ ├── glyphicons_042_group.png │ │ │ ├── glyphicons_079_podium.png │ │ │ ├── glyphicons_163_iphone.png │ │ │ ├── glyphicons_082_roundabout.png │ │ │ ├── glyphicons_266_book_open.png │ │ │ ├── glyphicons_214_resize_small.png │ │ │ └── glyphicons_155_show_thumbnails.png │ ├── ico │ │ ├── favicon.ico │ │ ├── bootstrap-apple-57x57.png │ │ ├── bootstrap-apple-72x72.png │ │ └── bootstrap-apple-114x114.png │ └── js │ │ ├── google-code-prettify │ │ └── prettify.css │ │ ├── bootstrap-transition.js │ │ ├── bootstrap-alert.js │ │ ├── bootstrap-dropdown.js │ │ ├── bootstrap-button.js │ │ ├── bootstrap-popover.js │ │ └── README.md ├── css │ ├── common.css │ ├── custom-theme │ │ └── images │ │ │ ├── ui-icons_004276_256x240.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_4b8e0b_256x240.png │ │ │ ├── ui-icons_a83300_256x240.png │ │ │ ├── ui-icons_cc0000_256x240.png │ │ │ ├── ui-icons_cccccc_256x240.png │ │ │ ├── ui-icons_ffffff_256x240.png │ │ │ ├── ui-bg_flat_0_333333_40x100.png │ │ │ ├── ui-bg_flat_30_cccccc_40x100.png │ │ │ ├── ui-bg_flat_50_5c5c5c_40x100.png │ │ │ ├── ui-bg_flat_65_ffffff_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_20_555555_1x400.png │ │ │ ├── ui-bg_glass_40_0078a3_1x400.png │ │ │ ├── ui-bg_glass_40_ffc73d_1x400.png │ │ │ ├── ui-bg_glass_55_fbf8ee_1x400.png │ │ │ ├── ui-bg_dots-small_65_a6a6a6_2x2.png │ │ │ ├── ui-bg_inset-soft_25_000000_1x100.png │ │ │ ├── ui-bg_inset-soft_30_c41200_1x100.png │ │ │ ├── ui-bg_gloss-wave_25_333333_500x100.png │ │ │ ├── ui-bg_highlight-soft_15_c41200_1x100.png │ │ │ ├── ui-bg_highlight-soft_80_eeeeee_1x100.png │ │ │ ├── ui-bg_diagonals-thick_75_f3d8d8_40x40.png │ │ │ ├── ui-bg_highlight-hard_100_eeeeee_1x100.png │ │ │ └── ui-bg_highlight-hard_100_f6f6f6_1x100.png │ ├── managers.css │ ├── ribbon3.css │ ├── live_log.css │ ├── live2.css │ ├── main.css │ └── fxc.css └── stylesheets │ └── default.css ├── .rvmrc.tcc ├── .gitignore ├── model ├── fxc_init.rb ├── disposition.rb ├── call_center │ ├── status_log.rb │ ├── tier.rb │ └── state_log.rb ├── call_record.rb ├── manager.rb ├── tiny_cdr_call.rb ├── init.rb └── db │ └── account.rb ├── localization.rb ├── migrations ├── 005_add_agent_to_call_records.rb ├── 008_add_admin_boolean_to_manager.rb ├── 002_add_eavesdrop_extension.rb ├── 001_create_managers.rb ├── optional │ ├── state_auditing │ │ ├── 20110206144000_create_state_log.rb │ │ ├── 20110222160000_new_state_trigger.rb │ │ └── 20110206144100_create_state_trigger.rb │ ├── status_auditing │ │ ├── 20110206144200_create_status_log.rb │ │ ├── 20110222160400_new_status_trigger.rb │ │ └── 20110206144300_create_status_trigger.rb │ ├── no_stat_resetting │ │ └── 20110206143900_postgres_no_reset.rb │ ├── pg_notifies │ │ ├── 20121220120000_basic_calls_notify.rb │ │ └── 20121220100000_detailed_calls_notify.rb │ └── abandon_auditing │ │ ├── 20110304102300_abandon_trigger.rb │ │ └── 20110304101800_create_abandon_log.rb ├── 007_create_accounts.rb ├── 004_call_records.rb ├── 006_add_left_right_to_call_records.rb └── 003_create_dispositions.rb ├── helper ├── fsr.rb └── flash.rb ├── node ├── main.rb ├── live_log.rb ├── ribbon2.rb ├── live.rb ├── live2.rb ├── queues.rb ├── ribbon.rb ├── accounts.rb ├── managers.rb └── fxc │ └── user.rb ├── view ├── fxc │ ├── index.xhtml │ └── user │ │ ├── delete.xhtml │ │ ├── create.xhtml │ │ └── index.xhtml ├── queues │ └── index.xhtml ├── nav.xhtml ├── accounts │ └── login.xhtml ├── index.xhtml ├── live_log │ └── index.xhtml ├── managers │ ├── index.xhtml │ └── new.xhtml ├── agents │ ├── index.xhtml │ ├── edit.xhtml │ └── new.xhtml ├── ribbon │ ├── v2.xhtml │ └── v3.xhtml └── tiers │ └── queue.xhtml ├── tasks ├── manifest.rake ├── yard.rake ├── install_dependencies.rake ├── setup.rake ├── reversion.rake ├── migrate.rake ├── gem.rake ├── rcov.rake ├── changelog.rake ├── schema.rake ├── copyright.rake ├── authors.rake ├── release.rake ├── bacon.rake └── gem_setup.rake ├── spec ├── tiny_call_center │ ├── model │ │ ├── manager.rb │ │ └── db_account.rb │ ├── fsr_integration.rb │ └── view │ │ ├── queue.rb │ │ ├── accounts.rb │ │ ├── agent.rb │ │ └── tier.rb └── helper.rb ├── lib ├── tiny_call_center │ ├── db.rb │ ├── utils │ │ ├── memory_backend.rb │ │ ├── memcached_backend.rb │ │ ├── redis_backend.rb │ │ └── fsr.rb │ ├── queue_router │ │ └── listener.rb │ ├── fs_listener.rb │ └── fs2ws │ │ └── channel_relay.rb └── tiny_call_center.rb ├── layout ├── accounts.xhtml ├── live.xhtml ├── live_log.xhtml ├── default.xhtml ├── managers.xhtml ├── fxc.xhtml ├── main.xhtml └── live2.xhtml ├── .travis.yml ├── bin ├── queue_router ├── notify.rb ├── beanstalk2ws ├── pg2beanstalk └── fs2ws ├── config.ru ├── .gems ├── README ├── fsconf ├── dialplan │ └── default │ │ └── 00_dialer_loop.xml └── 000_dialer_loop.xml ├── coffee ├── tiers.coffee └── fxc.coffee ├── app.rb └── Rakefile /sv/env/TCC_LogLevel: -------------------------------------------------------------------------------- 1 | INFO 2 | -------------------------------------------------------------------------------- /sv/env/TCC_OffHook: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /sv/env/TCC_QrPort: -------------------------------------------------------------------------------- 1 | 8884 2 | -------------------------------------------------------------------------------- /sv/queue_router/log/main: -------------------------------------------------------------------------------- 1 | ./ -------------------------------------------------------------------------------- /translation/en.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /sv/env/TCC_Innate_Port: -------------------------------------------------------------------------------- 1 | 7070 2 | -------------------------------------------------------------------------------- /sv/env/TCC_Monitors: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | -------------------------------------------------------------------------------- /sv/env/TCC_Server: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | -------------------------------------------------------------------------------- /sv/env/TCC_InternalProxy: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | -------------------------------------------------------------------------------- /sv/env/TCC_QrAddress: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | -------------------------------------------------------------------------------- /sv/env/TCC_Root: -------------------------------------------------------------------------------- 1 | /home/callcenter/g/tcc 2 | -------------------------------------------------------------------------------- /sv/queue_router/env: -------------------------------------------------------------------------------- 1 | /home/callcenter/.tcc_env -------------------------------------------------------------------------------- /sv/env/TCC_ProxyServerFormatString: -------------------------------------------------------------------------------- 1 | sofia/gateway/default/%s 2 | -------------------------------------------------------------------------------- /sv/env/TCC_WebSocketAgentURI: -------------------------------------------------------------------------------- 1 | ws://127.0.0.1:8080/websocket 2 | -------------------------------------------------------------------------------- /sv/env/TCC_WebSocketListenerURI: -------------------------------------------------------------------------------- 1 | ws://127.0.0.1:8081/websocket 2 | -------------------------------------------------------------------------------- /db/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | This file is to make sure the db/ dir gets created 2 | -------------------------------------------------------------------------------- /public/images/ear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/images/ear.png -------------------------------------------------------------------------------- /.rvmrc.tcc: -------------------------------------------------------------------------------- 1 | rvm --install --create use 2.0.0@tiny_call_center 2 | [[ -s .gems ]] && rvm gemset import .gems 3 | -------------------------------------------------------------------------------- /public/js/serenade.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/js/serenade.min.js.gz -------------------------------------------------------------------------------- /public/bootstrap/bootstrap.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/bootstrap.zip -------------------------------------------------------------------------------- /public/bootstrap/img/bird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/bird.png -------------------------------------------------------------------------------- /public/bootstrap/ico/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/ico/favicon.ico -------------------------------------------------------------------------------- /public/bootstrap/img/browsers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/browsers.png -------------------------------------------------------------------------------- /public/bootstrap/img/github-16px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/github-16px.png -------------------------------------------------------------------------------- /public/bootstrap/img/icon-css3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/icon-css3.png -------------------------------------------------------------------------------- /public/bootstrap/img/icon-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/icon-github.png -------------------------------------------------------------------------------- /public/bootstrap/img/icon-html5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/icon-html5.png -------------------------------------------------------------------------------- /public/bootstrap/img/less-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/less-small.png -------------------------------------------------------------------------------- /public/bootstrap/img/icon-twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/icon-twitter.png -------------------------------------------------------------------------------- /public/bootstrap/img/grid-18px-masked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/grid-18px-masked.png -------------------------------------------------------------------------------- /public/bootstrap/img/less-logo-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/less-logo-large.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-diagram-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-diagram-01.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-diagram-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-diagram-02.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-diagram-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-diagram-03.png -------------------------------------------------------------------------------- /public/css/common.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | background: #333; 6 | color: #fff; 7 | } 8 | -------------------------------------------------------------------------------- /public/bootstrap/ico/bootstrap-apple-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/ico/bootstrap-apple-57x57.png -------------------------------------------------------------------------------- /public/bootstrap/ico/bootstrap-apple-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/ico/bootstrap-apple-72x72.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/bartop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/bartop.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/fleetio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/fleetio.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/jshint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/jshint.png -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/kippt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/kippt.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/bootstrap/ico/bootstrap-apple-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/ico/bootstrap-apple-114x114.png -------------------------------------------------------------------------------- /public/bootstrap/img/bootstrap-mdo-sfmoma-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/bootstrap-mdo-sfmoma-01.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/bootstrap-mdo-sfmoma-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/bootstrap-mdo-sfmoma-02.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/bootstrap-mdo-sfmoma-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/bootstrap-mdo-sfmoma-03.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/railwayjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/railwayjs.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/bootstrap/img/responsive-illustrations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/responsive-illustrations.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .[a-z]* 2 | *~ 3 | log/* 4 | *.swp 5 | services/*/log/* 6 | services/*/supervise/* 7 | db/call_center.db 8 | migrations/optional/*.rb 9 | sv/* 10 | -------------------------------------------------------------------------------- /public/bootstrap/img/example-sites/totalwireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/example-sites/totalwireframe.png -------------------------------------------------------------------------------- /public/bootstrap/img/examples/bootstrap-example-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/examples/bootstrap-example-hero.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_009_magic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_009_magic.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_042_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_042_group.png -------------------------------------------------------------------------------- /public/bootstrap/img/examples/bootstrap-example-fluid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/examples/bootstrap-example-fluid.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_079_podium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_079_podium.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_163_iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_163_iphone.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_004276_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_004276_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_4b8e0b_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_4b8e0b_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_a83300_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_a83300_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_cccccc_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_cccccc_256x240.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /model/fxc_init.rb: -------------------------------------------------------------------------------- 1 | require_relative "../options" 2 | 3 | if root = TinyCallCenter.options.fxc.root 4 | require File.join(root, 'model/init') 5 | p FXC::Action.all 6 | end 7 | 8 | -------------------------------------------------------------------------------- /public/bootstrap/img/examples/bootstrap-example-starter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/examples/bootstrap-example-starter.jpg -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_082_roundabout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_082_roundabout.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_266_book_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_266_book_open.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_flat_0_333333_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_flat_0_333333_40x100.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_214_resize_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_214_resize_small.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_flat_30_cccccc_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_flat_30_cccccc_40x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_flat_50_5c5c5c_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_flat_50_5c5c5c_40x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_flat_65_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_flat_65_ffffff_40x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_glass_20_555555_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_glass_20_555555_1x400.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_glass_40_0078a3_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_glass_40_0078a3_1x400.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_glass_40_ffc73d_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_glass_40_ffc73d_1x400.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_glass_55_fbf8ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_glass_55_fbf8ee_1x400.png -------------------------------------------------------------------------------- /public/css/managers.css: -------------------------------------------------------------------------------- 1 | #managers { 2 | width: 100%; 3 | } 4 | 5 | #managers tr:hover { 6 | background: #222; 7 | } 8 | 9 | #managers a { 10 | color: #fff; 11 | } 12 | -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons/glyphicons_155_show_thumbnails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/bootstrap/img/glyphicons/glyphicons_155_show_thumbnails.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_dots-small_65_a6a6a6_2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_dots-small_65_a6a6a6_2x2.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_inset-soft_25_000000_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_inset-soft_25_000000_1x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_inset-soft_30_c41200_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_inset-soft_30_c41200_1x100.png -------------------------------------------------------------------------------- /public/css/ribbon3.css: -------------------------------------------------------------------------------- 1 | #makeCall, #callTransfer, #callDTMF { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | margin: 0; 6 | margin-left: -250px; 7 | margin-top: -4em; 8 | } 9 | -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_gloss-wave_25_333333_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_gloss-wave_25_333333_500x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_highlight-soft_15_c41200_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_highlight-soft_15_c41200_1x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_highlight-soft_80_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_highlight-soft_80_eeeeee_1x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_diagonals-thick_75_f3d8d8_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_diagonals-thick_75_f3d8d8_40x40.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_highlight-hard_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_highlight-hard_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /public/css/custom-theme/images/ui-bg_highlight-hard_100_f6f6f6_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyists/tiny_call_center/HEAD/public/css/custom-theme/images/ui-bg_highlight-hard_100_f6f6f6_1x100.png -------------------------------------------------------------------------------- /localization.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | 3 | module TCC 4 | DICTIONARY = Innate::Helper::Localize::Dictionary.new 5 | DICTIONARY.load(:en, yaml: 'translation/en.yaml') 6 | DICTIONARY.load(:de, yaml: 'translation/de.yaml') 7 | end 8 | -------------------------------------------------------------------------------- /migrations/005_add_agent_to_call_records.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | add_column :call_records, :agent, String 4 | end 5 | 6 | def down 7 | drop_column :call_records, :agent 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /migrations/008_add_admin_boolean_to_manager.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | add_column :managers, :admin, FalseClass 4 | end 5 | 6 | def down 7 | drop_column :managers, :admin 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /helper/fsr.rb: -------------------------------------------------------------------------------- 1 | module Innate 2 | module Helper 3 | module Fsr 4 | def fsr 5 | @fsr_command_socket ||= FSR::CommandSocket.new(server: ::TinyCallCenter.options.command_server) 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /migrations/002_add_eavesdrop_extension.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | add_column :managers, :eavesdrop_extension, String 4 | end 5 | 6 | def down 7 | drop_column :managers, :eavesdrop_extension 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /model/disposition.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class Disposition < Sequel::Model 3 | set_dataset TinyCallCenter.db[:dispositions] 4 | one_to_many :call_records 5 | def to_s 6 | "code: #{code}, key: #{key}, desc: #{description}" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sv/fs2ws/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | envdir=$PWD/env 3 | root=$(&1 12 | -------------------------------------------------------------------------------- /node/main.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class Main 3 | Innate.node '/', self 4 | helper :user 5 | layout :main 6 | trait :user_model => TinyCallCenter::Account 7 | 8 | def index 9 | redirect TCC::Accounts.r(:login) unless logged_in? 10 | @popup = true 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /view/fxc/index.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
5 | Name
#{h user.inspect}
17 | -------------------------------------------------------------------------------- /sv/fs2ws/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -w /var/log ];then 3 | service=$(basename $(dirname $(pwd))) 4 | [ -d /var/log/$service ] || mkdir -p /var/log/$service 5 | [ -L ./main ] || [ -d ./main ] || ln -s /var/log/$service ./main 6 | [ -L ./current ] || ln -s main/current 7 | exec svlogd -t ./main 8 | else 9 | exec svlogd -t ./ 10 | fi 11 | -------------------------------------------------------------------------------- /sv/tcc_innate/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -w /var/log ];then 3 | service=$(basename $(dirname $(pwd))) 4 | [ -d /var/log/$service ] || mkdir -p /var/log/$service 5 | [ -L ./main ] || [ -d ./main ] || ln -s /var/log/$service ./main 6 | [ -L ./current ] || ln -s main/current 7 | else 8 | ln -s ./ main 9 | fi 10 | exec svlogd -t ./main 11 | -------------------------------------------------------------------------------- /sv/pg2beanstalk/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -w /var/log ];then 3 | service=$(basename $(dirname $(pwd))) 4 | [ -d /var/log/$service ] || mkdir -p /var/log/$service 5 | [ -L ./main ] || [ -d ./main ] || ln -s /var/log/$service ./main 6 | [ -L ./current ] || ln -s main/current 7 | else 8 | ln -s ./ main 9 | fi 10 | exec svlogd -t ./main 11 | -------------------------------------------------------------------------------- /sv/queue_router/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -w /var/log ];then 3 | service=$(basename $(dirname $(pwd))) 4 | [ -d /var/log/$service ] || mkdir -p /var/log/$service 5 | [ -L ./main ] || [ -d ./main ] || ln -s /var/log/$service ./main 6 | [ -L ./current ] || ln -s main/current 7 | exec svlogd -t ./main 8 | else 9 | exec svlogd -t ./ 10 | fi 11 | -------------------------------------------------------------------------------- /tasks/manifest.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'update manifest' 6 | task :manifest do 7 | File.open('MANIFEST', 'w+'){|io| io.puts(*GEMSPEC.files) } 8 | end 9 | -------------------------------------------------------------------------------- /spec/tiny_call_center/model/manager.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../helper' 2 | 3 | describe 'TinyCallCenter Manager' do 4 | behaves_like :make_account 5 | 6 | it 'creates a manager' do 7 | manager = TCC::Manager.create(username: 'FooHoge') 8 | user = make_account('1234', 'bar', 'Foo', 'Hoge') 9 | 10 | user.manager?.should == true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /view/queues/index.xhtml: -------------------------------------------------------------------------------- 1 |
2 |

#{l 'Queues' }

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
#{l 'Name' }#{l 'Strategy' }
#{queue.name}#{queue.strategy}
15 |
16 | -------------------------------------------------------------------------------- /view/nav.xhtml: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /sv/queue_router/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | envdir=$PWD/env 3 | root=$(&1 15 | -------------------------------------------------------------------------------- /sv/tcc_innate/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | envdir=$PWD/env 3 | root=$(&1 13 | exec chpst -e $envdir thin -R config.ru -p $port start 2>&1 14 | -------------------------------------------------------------------------------- /lib/tiny_call_center/db.rb: -------------------------------------------------------------------------------- 1 | require 'sequel' 2 | require_relative "../../options" 3 | 4 | module TinyCallCenter 5 | @db ||= nil 6 | 7 | def self.db 8 | return @db if @db 9 | conn = TinyCallCenter.options.db 10 | Log.debug "Connecting to #{conn}" 11 | @db = Sequel.connect(conn) 12 | end 13 | 14 | def self.db=(other) 15 | @db = Sequel.connect(other) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /tasks/yard.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'Generate YARD documentation' 6 | task :yard => :clean do 7 | sh("yardoc -o ydoc --protected -r #{PROJECT_README} lib/**/*.rb tasks/*.rake") 8 | end 9 | -------------------------------------------------------------------------------- /tasks/install_dependencies.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'install dependencies' 6 | task :install_dependencies => [:gem_setup] do 7 | # GemInstaller.new do 8 | # setup_gemspec(GEMSPEC) 9 | # end 10 | end 11 | -------------------------------------------------------------------------------- /layout/accounts.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #{h @title} 6 | 7 | 8 | 9 | 10 | 11 | #{@content} 12 | 13 | 14 | -------------------------------------------------------------------------------- /view/accounts/login.xhtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: 'RUBYOPT=-rubygems rake bacon' 2 | before_script: 3 | - test -s "$HOME/.rvm/scripts/rvm" && source "$HOME/.rvm/scripts/rvm" 4 | - test -s .gems && rvm gemset import .gems 5 | - psql -U postgres -c 'create database tcc_spec;' 6 | env: TCC_DB=postgres://postgres@localhost/tcc_spec 7 | rvm: 8 | - 1.9.2 9 | - 1.9.3 10 | - ruby-head 11 | notifications: 12 | email: 13 | - mf@rubyists.com 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /view/fxc/user/delete.xhtml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /migrations/001_create_managers.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:managers) do 4 | primary_key :id 5 | String :username, :null => false 6 | String :include, :default => /^9998$/.to_s 7 | String :exclude, :default => /^((?:2[3456]|34)00|2613|3150)$/.to_s 8 | end unless DB.tables.include? :managers 9 | end 10 | 11 | def down 12 | remove_table(:managers) if DB.tables.include? :managers 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /node/live_log.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class LiveLog 3 | Innate.node "/live_log", self 4 | helper :user 5 | layout :live_log 6 | trait :user_model => TinyCallCenter::Account 7 | 8 | def index 9 | redirect Accounts.r(:login) unless logged_in? 10 | redirect Main.r(:index) unless user.manager? 11 | 12 | @agent = user.agent 13 | @server = TinyCallCenter.options.listener.server if request.local_net? 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/tiny_call_center/fsr_integration.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | 5 | require_relative '../helper' 6 | 7 | describe 'TinyCallCenter' do 8 | it "loads FSR" do 9 | FSR.should.respond_to?(:load_all_commands) 10 | FSR.should.respond_to?(:load_all_applications) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /migrations/optional/state_auditing/20110206144000_create_state_log.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:state_log) do 4 | primary_key :id 5 | String :agent, :null => false 6 | String :last_state, :null => false 7 | String :new_state, :null => false 8 | DateTime :created_at 9 | end unless DB.tables.include? :state_log 10 | end 11 | 12 | def down 13 | remove_table(:state_log) if DB.tables.include? :state_log 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /tasks/setup.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'install all possible dependencies' 6 | task :setup => :gem_setup do 7 | GemInstaller.new do 8 | # core 9 | 10 | # spec 11 | gem 'bacon' 12 | gem 'rcov' 13 | 14 | # doc 15 | gem 'yard' 16 | 17 | setup 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /migrations/optional/status_auditing/20110206144200_create_status_log.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:status_log) do 4 | primary_key :id 5 | String :agent, :null => false 6 | String :last_status, :null => false 7 | String :new_status, :null => false 8 | DateTime :created_at 9 | end unless DB.tables.include? :status_log 10 | end 11 | 12 | def down 13 | remove_table(:status_log) if DB.tables.include? :status_log 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /tasks/reversion.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc "update version.rb" 6 | task :reversion do 7 | File.open("lib/#{GEMSPEC.name}/version.rb", 'w+') do |file| 8 | file.puts("module #{PROJECT_MODULE}") 9 | file.puts(' VERSION = %p' % GEMSPEC.version.to_s) 10 | file.puts('end') 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /view/fxc/user/create.xhtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | { l 'User' } { l 'Details' } 4 | 5 | 6 | 7 |
8 | #{l 'Cancel'} 9 | 10 |
11 | -------------------------------------------------------------------------------- /node/ribbon2.rb: -------------------------------------------------------------------------------- 1 | require "fsr" 2 | require "fsr/command_socket" 3 | 4 | module TinyCallCenter 5 | class Ribbon2 6 | Innate.node "/ribbon2", self 7 | helper :user, :localize 8 | trait :user_model => TinyCallCenter::Account 9 | layout :ribbon2 10 | 11 | def index 12 | redirect Accounts.r(:login) unless logged_in? 13 | @agent = user.agent 14 | @extension = user.extension 15 | @server = TinyCallCenter.options.ribbon2.server if request.local_net? 16 | @title = @agent 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /sv/pg2beanstalk/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | exec 2>&1 3 | TERM=screen 4 | reg_serverdb=$(&1 16 | -------------------------------------------------------------------------------- /migrations/007_create_accounts.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:accounts) do 4 | primary_key :id 5 | String :username, null: false 6 | String :password, null: false 7 | String :first_name, null: false 8 | String :last_name, null: false 9 | String :extension, null: false 10 | String :registration_server, null: false 11 | end unless DB.tables.include? :accounts 12 | end 13 | 14 | def down 15 | remove_table(:accounts) if DB.tables.include? :accounts 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /view/index.xhtml: -------------------------------------------------------------------------------- 1 |
2 |

Tiny Call Center

3 | 21 |
22 | -------------------------------------------------------------------------------- /node/live.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class Live 3 | Innate.node "/live", self 4 | helper :user, :fsr, :localize 5 | layout :live 6 | trait :user_model => TinyCallCenter::Account 7 | 8 | def index 9 | redirect Accounts.r(:login) unless logged_in? 10 | redirect Main.r(:index) unless user.manager? 11 | 12 | @agent = user.agent 13 | @server = TinyCallCenter.options.listener.server if request.local_net? 14 | @couch_uri = TinyCallCenter.options.tiny_cdr.couch_uri 15 | @title = @agent 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /node/live2.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class Live2 3 | Innate.node "/live2", self 4 | helper :user, :fsr, :localize 5 | layout :live2 6 | trait :user_model => TinyCallCenter::Account 7 | 8 | def index 9 | redirect Accounts.r(:login) unless logged_in? 10 | redirect Main.r(:index) unless user.manager? 11 | 12 | @agent = user.agent 13 | @server = TinyCallCenter.options.live2.server if request.local_net? 14 | @couch_uri = TinyCallCenter.options.tiny_cdr.couch_uri 15 | @title = @agent 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/tiny_call_center.rb: -------------------------------------------------------------------------------- 1 | require "pathname" 2 | require 'log4r' 3 | require 'log4r/configurator' 4 | 5 | Log4r::Configurator.custom_levels(:DEBUG, :DEVEL, :INFO, :NOTICE, :WARN, :ERROR, :CRIT) 6 | 7 | require 'fsr' 8 | 9 | class Pathname 10 | def /(other) 11 | join(other.to_s) 12 | end 13 | end 14 | 15 | module TinyCallCenter 16 | Log = FSR::Log 17 | ROOT = (Pathname(__FILE__)/'../..').expand_path 18 | LIBROOT = ROOT/:lib 19 | MIGRATION_ROOT = ROOT/:migrations 20 | MODEL_ROOT = ROOT/:model 21 | SPEC_HELPER_PATH = ROOT/:spec 22 | end 23 | 24 | TCC = TinyCallCenter 25 | -------------------------------------------------------------------------------- /public/js/sort_table.js: -------------------------------------------------------------------------------- 1 | var directions = {}; 2 | function sorter(trigger, sortOn, sortType){ 3 | trigger.click(function(event){ 4 | var table = $(event.target).closest('.table') 5 | var direction = directions[sortOn] === 'asc' ? 'desc' : 'asc'; 6 | directions[sortOn] = direction; 7 | table.sort({ 8 | sortOn: sortOn, 9 | direction: direction, 10 | sortType: sortType, 11 | }); 12 | table.prepend($('.head', table)); 13 | $('.head > *', table).removeClass('asc'); 14 | $('.head > *', table).removeClass('desc'); 15 | trigger.addClass(direction); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /view/live_log/index.xhtml: -------------------------------------------------------------------------------- 1 |
2 | Suspend 3 | resume 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
#{l 'Action'}#{l 'Agent' }#{l 'Detail' }
17 | 18 |
19 |
20 | 21 |
22 | #{h @agent} 23 | #{h @server} 24 | 25 |
26 | -------------------------------------------------------------------------------- /lib/tiny_call_center/utils/memory_backend.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module MemoryBackend 3 | def initialize(*args, &block) 4 | @channel_answers, @channel_originates = {}, {} 5 | super 6 | end 7 | 8 | def set_originate(uuid, msg) 9 | @channel_originates[uuid] = msg 10 | end 11 | 12 | def get_originate(uuid) 13 | @channel_originates[uuid] 14 | end 15 | 16 | def set_answer(uuid, msg) 17 | @channel_answers[uuid] = msg 18 | end 19 | 20 | def get_answer(uuid) 21 | @channel_answers[uuid] 22 | end 23 | end 24 | end 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /bin/queue_router: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # $VERBOSE = 2 3 | 4 | require 'pp' 5 | 6 | require_relative '../lib/tiny_call_center' 7 | require_relative '../options' 8 | require_relative '../model/init' 9 | 10 | require "fsr/listener/outbound" 11 | FSR.load_all_applications 12 | TCC::Log.level = Log4r.const_get(TCC.options.log_level) 13 | 14 | require_relative '../lib/tiny_call_center/queue_router/listener' 15 | 16 | port = TinyCallCenter.options.qr_port.to_i 17 | addr = TinyCallCenter.options.qr_addr 18 | 19 | $stdout.sync = true 20 | EM.epoll? 21 | FSR.start_oes! TCC::QueueRouter::Listener, :port => port, :host => addr 22 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rackup 2 | # 3 | # config.ru for ramaze apps 4 | # use thin >= 1.0.0 5 | # thin start -R config.ru 6 | # 7 | # rackup is a useful tool for running Rack applications, which uses the 8 | # Rack::Builder DSL to configure middleware and build up applications easily. 9 | # 10 | # rackup automatically figures out the environment it is run in, and runs your 11 | # application as FastCGI, CGI, or standalone with Mongrel or WEBrick -- all from 12 | # the same configuration. 13 | 14 | require ::File.expand_path('app', ::File.dirname(__FILE__)) 15 | Innate.start(:file => __FILE__, :started => true) 16 | run Innate 17 | -------------------------------------------------------------------------------- /model/call_center/status_log.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module CallCenter 3 | class StatusLog < Sequel::Model 4 | set_dataset FSCallCenter.db[:status_log] 5 | 6 | def self.agent_history(agent, from = Date.today, to = nil) 7 | ds = filter{{:agent => agent} & (created_at > from)} 8 | ds = ds.filter{(created_at < to)} if to 9 | ds.select(:new_status, :created_at).order_by(:created_at.desc) 10 | end 11 | 12 | def self.agent_history_a(agent, from = Date.today, to = nil) 13 | agent_history(agent, from, to).map(&:values) 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /view/managers/index.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
#{l 'Name' }#{l 'Include' }#{l 'Exclude' }  
#{h manager.username}#{h manager.include}#{h manager.exclude}#{a "#{l 'Edit' }", :edit, manager.id, manager.username}#{a "#{l 'Delete' }", :delete, manager.id, manager.username}
19 | -------------------------------------------------------------------------------- /migrations/004_call_records.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:call_records) do 4 | primary_key :id 5 | String :caller_id_number, :null => false 6 | String :caller_id_name 7 | String :destination_number, :null => false 8 | String :queue_name 9 | String :uuid, :null => false, :unique => true 10 | String :channel, :null => false 11 | DateTime :created_at 12 | foreign_key :disposition_id, :dispositions 13 | end unless DB.tables.include? :call_records 14 | end 15 | 16 | def down 17 | remove_table(:call_records) if DB.tables.include? :call_records 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /bin/notify.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "em-postgres" 3 | channels = %w{channel_insert channel_update channel_delete} 4 | EventMachine.run { 5 | conn = EventMachine::Postgres.new(:database => 'freeswitch', :port => 5435) 6 | channels.each do |channel| 7 | q = conn.execute("LISTEN #{channel};") { |res| p channel + " Subscribed" } 8 | q.errback{|r| warn "Error: %s" % r } 9 | end 10 | 11 | EventMachine.add_timer(1) do 12 | EventMachine.add_periodic_timer(0) { 13 | p 'Waiting for notifications' 14 | conn.wait_for_notify(*[]) { |chan, pid, payload| p ['Notify Received', chan, pid, payload]; $stdout.flush } 15 | } 16 | end 17 | } 18 | -------------------------------------------------------------------------------- /model/call_center/tier.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module CallCenter 3 | class Tier < Sequel::Model 4 | set_dataset FSCallCenter.db[:tiers] 5 | 6 | def self.extension_primary_queue(extension) 7 | if account = TCC::Account.from_extension(extension) 8 | agent_primary_queue(account.agent) 9 | else 10 | false 11 | end 12 | end 13 | 14 | def self.agent_primary_queue(agent) 15 | sub = filter(:agent => agent).select(:queue) 16 | ds = filter(:queue => sub).select(:queue, :count.sql_function("*")).group(:queue).order(:count.desc) 17 | queue = ds.first 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/tiny_call_center/queue_router/listener.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module QueueRouter 3 | class Listener < FSR::Listener::Outbound 4 | def session_initiated 5 | destination = @session.headers[:caller_destination_number] 6 | Log.info "<< Session initiated to #{destination} >>" 7 | if queue = TCC::CallCenter::Tier.extension_primary_queue(destination) 8 | name = queue[:queue] 9 | else 10 | name = "7171" 11 | end 12 | pre_answer do 13 | Log.info "<< Transfer to #{name} XML default >>" 14 | transfer(name, "XML", "default") { close_connection } 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /node/queues.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2011 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | module TinyCallCenter 6 | class Queues 7 | Innate.node "/queues", self 8 | helper :user, :fsr, :localize 9 | layout :default 10 | trait :user_model => TinyCallCenter::Account 11 | 12 | trait queues: nil 13 | 14 | before_all do 15 | redirect TCC::Accounts.r(:login) unless logged_in? 16 | end 17 | 18 | def index(queue_name = nil) 19 | @queues = ancestral_trait[:queues] || 20 | fsr.call_center(:queue).list(queue_name).run 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.gems: -------------------------------------------------------------------------------- 1 | # .gems generated gem export file. Note that any env variable settings will be missing. Append these after using a ';' field separator 2 | 3 | addressable -v2.3.5 4 | bacon -v1.2.0 5 | beanstalk-client -v1.1.1 6 | daemons -v1.1.9 7 | diff-lcs -v1.2.5 8 | em-jack -v0.1.5 9 | em-spec -v0.2.6 10 | em-websocket -v0.5.0 11 | eventmachine -v1.0.3 12 | freeswitcher -v0.8.0 13 | innate -v2013.02.21 14 | locale -v2.0.9 15 | log4r -v1.1.10 16 | name_parse -v0.0.5 17 | nokogiri -v1.6.0 18 | pg -v0.17.0 19 | pgpass -v2012.01.18 20 | rack -v1.5.2 21 | rack-test -v0.6.2 22 | rspec -v2.14.1 23 | rspec-core -v2.14.7 24 | rspec-expectations -v2.14.4 25 | rspec-mocks -v2.14.4 26 | ruby-ldap -v0.9.16 27 | sequel -v4.4.0 28 | sqlite3 -v1.3.8 29 | test-unit -v2.5.5 30 | thin -v1.6.1 31 | -------------------------------------------------------------------------------- /public/css/live_log.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | #pause, #play { 8 | color: #fff; 9 | } 10 | 11 | .line { 12 | background: rgba(255,255,255,0.1); 13 | } 14 | 15 | .line.active { 16 | background: rgba(255,0,0,0.1); 17 | } 18 | 19 | dl { 20 | margin: 0; 21 | } 22 | 23 | #log { 24 | width: 40%; 25 | height: 90%; 26 | overflow: auto; 27 | background: #111; 28 | display: block; 29 | float: left; 30 | } 31 | 32 | #detail { 33 | width: 60%; 34 | height: 90%; 35 | overflow: auto; 36 | background: #111; 37 | display: block; 38 | float: right; 39 | } 40 | 41 | footer { 42 | display: block; 43 | text-align: center; 44 | font-size: 0.9em; 45 | } 46 | 47 | #agent_name, #server { 48 | display: none; 49 | } 50 | -------------------------------------------------------------------------------- /node/ribbon.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2011 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | require "fsr" 6 | require "fsr/command_socket" 7 | 8 | module TinyCallCenter 9 | class Ribbon 10 | Innate.node "/ribbon", self 11 | helper :user 12 | helper :localize 13 | trait :user_model => TinyCallCenter::Account 14 | 15 | def index 16 | redirect Accounts.r(:login) unless logged_in? 17 | @agent = user.agent 18 | @extension = user.extension 19 | @server = TinyCallCenter.options.ribbon.server if request.local_net? 20 | @title = @agent 21 | end 22 | 23 | alias v2 index 24 | alias v3 index 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /model/call_record.rb: -------------------------------------------------------------------------------- 1 | require "date" 2 | module TinyCallCenter 3 | class CallRecord < Sequel::Model 4 | set_dataset TinyCallCenter.db[:call_records] 5 | many_to_one :disposition 6 | 7 | def self.last(username) 8 | CallRecord.filter(agent: username).order(Sequel.desc(:created_at)).limit(1).first 9 | end 10 | 11 | def self.agent_history(agent, from = Date.today, to = nil) 12 | ds = filter{{:agent => agent} & (created_at > from)} 13 | ds = filter{(created_at < to)} if to 14 | ds.order_by(:created_at.desc) 15 | end 16 | 17 | def self.agent_history_a(agent, from = Date.today, to = nil) 18 | agent_history(agent, from, to).map(&:values) 19 | end 20 | 21 | def validate 22 | self[:created_at] = DateTime.now unless self[:created_at] 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Applications which allow monitoring of FreeSWITCH, plus management and administration 2 | of FreeSWITCH's mod_callcenter queue routing. 3 | 4 | Features: 5 | 6 | * Agent Ribbon - for agents to see current calls, transfer them, set themselves 7 | Ready/Wrap-Up and Available/On-Break/Logged Out in mod_callcenter 8 | 9 | * Real-Time Manager View - for managers to see what their agents are doing, as well 10 | as change agents' status/state and eavesdrop calls 11 | 12 | * Mod_Callcenter Admin - For adding agents to mod_callcenter, and manipulating Tiers 13 | 14 | On The Way: 15 | 16 | * Report Interface 17 | 18 | * TinyCdr Integration 19 | 20 | * TinyDialer predictive/interactive dialing 21 | 22 | * FXC - Full FreeSWITCH configuration framework 23 | 24 | 25 | See Also: https://github.com/rubyists/tiny_call_center/wiki 26 | -------------------------------------------------------------------------------- /node/accounts.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | module TinyCallCenter 6 | class Accounts 7 | Innate.node "/accounts", self 8 | helper :user 9 | helper :localize 10 | trait :user_model => TinyCallCenter::Account 11 | layout :main 12 | 13 | def login 14 | Log.info "Attempt login with backend: %p" % [TCC.options.backend] 15 | return unless request.post? 16 | user_login(request.subset(:name, :pass)) 17 | Log.info "Login successful: #{logged_in?}" 18 | redirect Main.r('/') 19 | end 20 | 21 | def logout 22 | user_logout 23 | redirect_referer 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /tasks/migrate.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | 5 | desc "migrate to latest version of db" 6 | task :migrate, :version do |_, args| 7 | args.with_defaults(:version => nil) 8 | require_relative '../lib/tiny_call_center' 9 | require_relative '../lib/tiny_call_center/db' 10 | require 'sequel' 11 | require 'sequel/extensions/migration' 12 | raise "No DB found" unless DB = TinyCallCenter.db 13 | 14 | if args.version.nil? 15 | Sequel::Migrator.apply(TinyCallCenter.db, TinyCallCenter::MIGRATION_ROOT) 16 | else 17 | Sequel::Migrator.apply(TinyCallCenter.db, TinyCallCenter::MIGRATION_ROOT, :target => args.version.to_i) 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /tasks/gem.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2011 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | 5 | desc "make a gemspec" 6 | task :gemspec => [:manifest, :changelog, :authors] do 7 | gemspec_file = "#{GEMSPEC.name}.gemspec" 8 | File.open(gemspec_file, 'w+'){|gs| gs.puts(GEMSPEC.to_ruby) } 9 | end 10 | 11 | desc "package and install from gemspec" 12 | task :install => [:gemspec] do 13 | sh "gem build #{GEMSPEC.name}.gemspec" 14 | sh "gem install #{GEMSPEC.name}-#{GEMSPEC.version}.gem" 15 | end 16 | 17 | desc "uninstall the gem" 18 | task :uninstall => [:clean] do 19 | sh %{gem uninstall -x #{GEMSPEC.name}} 20 | end 21 | 22 | Gem::PackageTask.new(GEMSPEC) do |pkg| 23 | pkg.need_tar = true 24 | end 25 | -------------------------------------------------------------------------------- /tasks/rcov.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'code coverage' 6 | task :rcov => :clean do 7 | specs = PROJECT_SPECS 8 | 9 | ignore = %w[ gem rack bacon innate hpricot nagoro/lib/nagoro ] 10 | 11 | if RUBY_VERSION >= '1.8.7' 12 | ignore << 'begin_with' << 'end_with' 13 | end 14 | if RUBY_VERSION < '1.9' 15 | ignore << 'fiber' 16 | end 17 | 18 | ignored = ignore.join(',') 19 | 20 | cmd = "rcov --aggregate coverage.data --sort coverage -t --%s -x '#{ignored}' %s" 21 | 22 | while spec = specs.shift 23 | puts '', "Gather coverage for #{spec} ..." 24 | html = specs.empty? ? 'html' : 'no-html' 25 | sh(cmd % [html, spec]) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /model/call_center/state_log.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module CallCenter 3 | class StateLog < Sequel::Model 4 | set_dataset FSCallCenter.db[:state_log] 5 | 6 | def self.agent_history_a(agent, from = Date.today, to = nil) 7 | agent_history(agent, from, to).map { |row| 8 | v = row.values 9 | 10 | case v[:new_state] 11 | when 'Waiting' 12 | v[:new_state] = 'Ready' 13 | when 'Idle' 14 | v[:new_state] = 'Wrap-up' 15 | end 16 | 17 | v 18 | } 19 | end 20 | 21 | def self.agent_history(agent, from = Date.today, to = nil) 22 | ds = filter{{:agent => agent} & (created_at > from)} 23 | ds = ds.filter{(created_at < to)} if to 24 | ds.select(:new_state, :created_at).order_by(:created_at.desc) 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /layout/live.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Live CallCenter · #{h @agent} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | #@content 17 | 18 | 19 | -------------------------------------------------------------------------------- /layout/live_log.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Live Log · #{h @agent} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | #@content 17 | 18 | 19 | -------------------------------------------------------------------------------- /tasks/changelog.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | desc 'update changelog' 6 | task :changelog do 7 | File.open('CHANGELOG', 'w+') do |changelog| 8 | `git log -z --abbrev-commit`.split("\0").each do |commit| 9 | next if commit =~ /^Merge: \d*/ 10 | ref, author, time, _, title, _, message = commit.split("\n", 7) 11 | ref = ref[/commit ([0-9a-f]+)/, 1] 12 | author = author[/Author: (.*)/, 1].strip 13 | time = Time.parse(time[/Date: (.*)/, 1]).utc 14 | title.strip! 15 | 16 | changelog.puts "[#{ref} | #{time}] #{author}" 17 | changelog.puts '', " * #{title}" 18 | changelog.puts '', message.rstrip if message 19 | changelog.puts 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /model/manager.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | class Manager < Sequel::Model 3 | set_dataset TinyCallCenter.db[:managers] 4 | 5 | # Check the includes and excludes to make sure the caller (cid_to) 6 | # or the callee (cid) are included in what the user is allowed to listen to 7 | # Then make sure the callee and caller arent in the users' excluded extensions 8 | # both self.include and self.exclude are regular expression in string format 9 | # as created by /^(1234)$/.to_s type notation, cid and cid_to are expected to 10 | # be strings 11 | def authorized_to_listen?(cid, cid_to) 12 | includes = Regexp.new(include) 13 | excludes = Regexp.new(exclude) if exclude 14 | 15 | if excludes 16 | (cid =~ includes || cid_to =~ includes) && 17 | !(cid =~ excludes || cid_to =~ excludes) 18 | else 19 | cid =~ includes || cid_to =~ includes 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /model/tiny_cdr_call.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module TinyCdr 3 | class Call < Sequel::Model 4 | set_dataset TinyCdr.db[:calls] 5 | 6 | def self.last(extension) 7 | history(extension).limit(1).first 8 | end 9 | 10 | def self.history(extension, from = Date.today, to = nil) 11 | Log.debug("Extension is %s" % extension) 12 | ds = filter("(username = '#{extension}' or destination_number = '#{extension}') and (start_stamp > '#{from}')") 13 | ds = ds.filter{(start_stamp < to)} if to 14 | ds.order_by(Sequel.desc(:start_stamp)) 15 | end 16 | 17 | def destination 18 | if destination_number =~ /^\d\d{8,}$/ 19 | destination_number[-10,10] 20 | else 21 | destination_number 22 | end 23 | end 24 | 25 | def queue_call? 26 | channel =~ /#{Regexp.escape(TinyCallCenter.options.command_server)}(:\d\d{1,3})?$/ 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /public/bootstrap/js/google-code-prettify/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .linenums .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 18px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /migrations/006_add_left_right_to_call_records.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | add_column :call_records, :left_cid_num, String 4 | add_column :call_records, :left_cid_name, String 5 | add_column :call_records, :left_destination, String 6 | add_column :call_records, :left_channel, String 7 | add_column :call_records, :left_uuid, String 8 | add_column :call_records, :right_cid_num, String 9 | add_column :call_records, :right_cid_name, String 10 | add_column :call_records, :right_destination, String 11 | add_column :call_records, :right_channel, String 12 | add_column :call_records, :right_uuid, String 13 | drop_column :call_records, :caller_id_number 14 | drop_column :call_records, :caller_id_name 15 | drop_column :call_records, :channel 16 | drop_column :call_records, :destination_number 17 | drop_column :call_records, :uuid 18 | end 19 | 20 | def down 21 | raise "Cannot go down from here" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /migrations/optional/no_stat_resetting/20110206143900_postgres_no_reset.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | execute <<-SQL 4 | CREATE OR REPLACE FUNCTION no_reset() RETURNS trigger AS $no_reset$ 5 | BEGIN 6 | -- if we're an admin, let any update happen 7 | IF current_user = 'callcenter_admin' THEN 8 | RETURN NEW; 9 | END IF; 10 | -- Otherwise, Check for changes to stats, don't let any of these decrement 11 | IF NEW.talk_time < OLD.talk_time THEN 12 | NEW.talk_time := OLD.talk_time; 13 | END IF; 14 | IF NEW.calls_answered < OLD.calls_answered THEN 15 | NEW.calls_answered := OLD.calls_answered; 16 | END IF; 17 | RETURN NEW; 18 | END; 19 | $no_reset$ LANGUAGE plpgsql; 20 | 21 | CREATE TRIGGER no_reset BEFORE UPDATE ON agents 22 | FOR EACH ROW EXECUTE PROCEDURE no_reset(); 23 | SQL 24 | end 25 | 26 | def down 27 | raise "Cannot go down from here" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /layout/default.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tiny Call Center — #{h @title} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Tiny Call Center — #{h @title}

16 | #{TCC::Main.render_view(:nav)} 17 | #{flashbox} 18 |
19 |
20 | #@content 21 |
22 |
23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/tiny_call_center/utils/memcached_backend.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module MemcachedBackend 3 | def initialize(*args, &block) 4 | begin 5 | require "memcache" 6 | rescue LoadError => ex 7 | p ex 8 | end 9 | 10 | @originate_cache = MemCache.new(TCC.options.memcached.servers, prefix_key: 'orig_') 11 | @answer_cache = MemCache.new(TCC.options.memcached.servers, prefix_key: 'answer_') 12 | 13 | super 14 | rescue => ex 15 | puts ex, ex.backtrace 16 | Log.error(ex) 17 | abort "Error in memcached: #{ex}" 18 | end 19 | 20 | def set_originate(uuid, msg) 21 | @originate_cache.set("#{uuid}", msg) 22 | end 23 | 24 | def get_originate(uuid) 25 | @originate_cache.get("#{uuid}", nil) rescue nil 26 | end 27 | 28 | def set_answer(uuid, msg) 29 | @answer_cache.set("#{uuid}", msg) 30 | end 31 | 32 | def get_answer(uuid) 33 | @answer_cache.get("#{uuid}", nil) rescue nil 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /layout/managers.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tiny Call Center — #{h @title} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Tiny Call Center — #{h @title}

16 | #{TCC::Main.render_view(:nav)} 17 | #{flashbox} 18 |
19 |
20 | #@content 21 |
22 |
23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/tiny_call_center/utils/redis_backend.rb: -------------------------------------------------------------------------------- 1 | module TinyCallCenter 2 | module RedisBackend 3 | def initialize(*args, &block) 4 | require "redis" 5 | 6 | options = TCC.options.redis 7 | host, port = options.server.split(':') 8 | 9 | @originate_cache = Redis.new(host: host, port: port) 10 | @answer_cache = Redis.new(host: host, port: port) 11 | @ttl = options.ttl 12 | 13 | super 14 | rescue => ex 15 | puts ex, ex.backtrace 16 | Log.error(ex) 17 | abort "Error in redis init: #{ex}" 18 | end 19 | 20 | def set_originate(uuid, msg) 21 | key = "originate_#{uuid}" 22 | @originate_cache.setex(key, @ttl, msg) 23 | msg 24 | end 25 | 26 | def get_originate(uuid) 27 | @originate_cache["originate_#{uuid}"] 28 | end 29 | 30 | def set_answer(uuid, msg) 31 | key = "answer_#{uuid}" 32 | @answer_cache.setex(key, @ttl, msg) 33 | msg 34 | end 35 | 36 | def get_answer(uuid) 37 | @answer_cache["answer_#{uuid}"] 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /migrations/003_create_dispositions.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | create_table(:dispositions) do 4 | primary_key :id 5 | String :code, :null => false 6 | String :key, :null => false 7 | String :description, :null => false 8 | end unless DB.tables.include? :dispositions 9 | [["900", "F1", "Talked To Right Party"], 10 | ["901", "F2", "Left Message On Machine"], 11 | ["902", "F3", "Left Message With 3rd Party"], 12 | ["903", "F4", "Promise Made"], 13 | ["904", "F5", "Payment Made"], 14 | ["905", "F6", "Disconnected Number"], 15 | ["906", "F7", "No Answer"], 16 | ["907", "F8", "Disputed Debt"], 17 | ["908", "F9", "Wrong Number"], 18 | ["909", "F10", "Busy/Fax"], 19 | ["910", "F11", "No Calls To POE"], 20 | ["911", "F12", "Transfer To Spanish Speaker"]].each { |row| 21 | DB[:dispositions].insert(code: row[0], key: row[1], description: row[2]) 22 | } 23 | end 24 | 25 | def down 26 | remove_table(:dispositions) if DB.tables.include? :dispositions 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /layout/fxc.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FXC 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

#{TCC::FXC::User.a('FXC', :/)}

17 | 22 |
23 |
24 | #{@content} 25 |
26 |
27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /migrations/optional/state_auditing/20110222160000_new_state_trigger.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | execute <<-SQL 4 | CREATE OR REPLACE FUNCTION log_state_change() RETURNS TRIGGER AS $state_change$ 5 | BEGIN 6 | -- 7 | -- Create a row in state_log to reflect the state change of agents, 8 | -- make use of the special variable TG_OP to work out the operation. 9 | -- 10 | IF (TG_OP = 'UPDATE') THEN 11 | IF OLD.state <> NEW.state THEN 12 | INSERT INTO state_log SELECT nextval('state_log_id_seq'), OLD.name, OLD.state, NEW.state, now(); 13 | RETURN NEW; 14 | END IF; 15 | ELSIF (TG_OP = 'INSERT') THEN 16 | INSERT INTO state_log SELECT nextval('state_log_id_seq'), NEW.name, 'initial', NEW.state, now(); 17 | RETURN NEW; 18 | END IF; 19 | RETURN NULL; -- result is ignored since this is an AFTER trigger 20 | END; 21 | $state_change$ LANGUAGE plpgsql; 22 | 23 | SQL 24 | end 25 | 26 | def down 27 | raise "Cannot Go Down from here" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /migrations/optional/status_auditing/20110222160400_new_status_trigger.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | execute <<-SQL 4 | CREATE OR REPLACE FUNCTION log_status_change() RETURNS TRIGGER AS $status_change$ 5 | BEGIN 6 | -- 7 | -- Create a row in status_log to reflect the status change of agents, 8 | -- make use of the special variable TG_OP to work out the operation. 9 | -- 10 | IF (TG_OP = 'UPDATE') THEN 11 | IF OLD.status <> NEW.status THEN 12 | INSERT INTO status_log SELECT nextval('status_log_id_seq'), OLD.name, OLD.status, NEW.status, now(); 13 | RETURN NEW; 14 | END IF; 15 | ELSIF (TG_OP = 'INSERT') THEN 16 | INSERT INTO status_log SELECT nextval('status_log_id_seq'), NEW.name, 'initial', NEW.status, now(); 17 | RETURN NEW; 18 | END IF; 19 | RETURN NULL; -- result is ignored since this is an AFTER trigger 20 | END; 21 | $status_change$ LANGUAGE plpgsql; 22 | SQL 23 | end 24 | 25 | def down 26 | raise "Cannot go down from here" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /tasks/schema.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | task :test_db do 6 | require_relative "../lib/fxc" 7 | require FXC::ROOT/:spec/:db_helper 8 | end 9 | 10 | desc "Dump the test schema" 11 | task :schema, [:format] => [:test_db] do |t,args| 12 | args.with_defaults(:format => "html") 13 | descs = FXC.db.tables.inject([]) do |arr, table| 14 | arr << "\\dd #{table};\\d+ #{table}" 15 | end 16 | commands = descs.join(";") 17 | if args.format.to_s == "html" 18 | f = File.open("doc/schema.html","w+") 19 | command = %Q{echo '\\H #{commands}'|PGDATA=#{ENV['PGDATA']} PGHOST=#{ENV['PGHOST']} PGPORT=#{ENV['PGPORT']} psql fxc|tail -n +2} 20 | else 21 | command = %Q{echo '#{commands}'|PGDATA=#{ENV['PGDATA']} PGHOST=#{ENV['PGHOST']} PGPORT=#{ENV['PGPORT']} psql fxc} 22 | f = $stdout 23 | end 24 | f.puts %x{#{command}} 25 | unless f == $stdout 26 | f.close 27 | puts "Saved doc/schema.html" 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /layout/main.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tiny Call Center 6 | 7 | 8 | 9 | 31 | 32 | 33 | #@content 34 | 35 | 36 | -------------------------------------------------------------------------------- /view/managers/new.xhtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | #{h @legend} 4 | 5 |
6 | #{l 'Username' } 7 | 16 |
17 | 18 |
19 | #{l 'Include' } 20 | 21 |
22 | 23 |
24 | #{l 'Exclude' } 25 | 26 |
27 | 28 |
29 | 30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /fsconf/dialplan/default/00_dialer_loop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /migrations/optional/state_auditing/20110206144100_create_state_trigger.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | execute <<-SQL 4 | CREATE OR REPLACE FUNCTION log_state_change() RETURNS TRIGGER AS $state_change$ 5 | BEGIN 6 | -- 7 | -- Create a row in state_log to reflect the state change of agents, 8 | -- make use of the special variable TG_OP to work out the operation. 9 | -- 10 | IF (TG_OP = 'UPDATE') THEN 11 | IF OLD.state <> NEW.state THEN 12 | INSERT INTO state_log SELECT nextval('state_log_id_seq'), OLD.name, OLD.state, NEW.state, now(); 13 | RETURN NEW; 14 | END IF; 15 | ELSIF (TG_OP = 'INSERT') THEN 16 | INSERT INTO state_log SELECT nextval('state_log_id_seq'), OLD.name, 'initial', NEW.state, now(); 17 | RETURN NEW; 18 | END IF; 19 | RETURN NULL; -- result is ignored since this is an AFTER trigger 20 | END; 21 | $state_change$ LANGUAGE plpgsql; 22 | CREATE TRIGGER state_log_trigger 23 | AFTER INSERT OR UPDATE ON agents 24 | FOR EACH ROW EXECUTE PROCEDURE log_state_change(); 25 | SQL 26 | end 27 | 28 | def down 29 | execute <<-SQL 30 | DROP TRIGGER state_log_trigger on agents; 31 | DROP FUNCTION log_state_change; 32 | SQL 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /bin/beanstalk2ws: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../lib/tiny_call_center" 4 | require TCC::ROOT/:options 5 | require TCC::LIBROOT/"tiny_call_center/ribbon_web_socket" 6 | require TCC::LIBROOT/"tiny_call_center/fs_listener" 7 | require TCC::LIBROOT/"tiny_call_center/live_web_socket" 8 | require TCC::LIBROOT/"tiny_call_center/jack_tube" 9 | 10 | TCC::Log.level = Log4r.const_get(TCC.options.log_level) 11 | p = Log4r::PatternFormatter.new :pattern => '%l <%x> %m' 12 | TCC::Log.outputters.each { |outputter| outputter.formatter = p } 13 | 14 | with_log = lambda do |name, &block| 15 | begin 16 | Log4r::NDC.push name 17 | block.call 18 | ensure 19 | Log4r::NDC.pop 20 | end 21 | end 22 | 23 | websocket = lambda do |uri, handler| 24 | host, port = uri.host, uri.port 25 | 26 | with_log.('websocket') do 27 | TCC::Log.info "Starting Websocket on #{host}:#{port}" 28 | 29 | EM::WebSocket.start(host: host, port: port, debug: false) do |ws| 30 | handler.new(ws) 31 | end 32 | end 33 | end 34 | 35 | EM.run do 36 | websocket.(TCC.options.live2.server, TCC::LiveManager) 37 | websocket.(TCC.options.ribbon2.server, TCC::RibbonAgent) 38 | 39 | with_log.('jack_tube'){ 40 | tubes = TCC.options.beanstalk.listen_tubes 41 | TCC::Log.info "Starting JackTube for #{tubes}" 42 | TCC::JackTube.new.watch_socket(tubes) 43 | } 44 | end 45 | -------------------------------------------------------------------------------- /migrations/optional/status_auditing/20110206144300_create_status_trigger.rb: -------------------------------------------------------------------------------- 1 | Class.new Sequel::Migration do 2 | def up 3 | execute <<-SQL 4 | CREATE OR REPLACE FUNCTION log_status_change() RETURNS TRIGGER AS $status_change$ 5 | BEGIN 6 | -- 7 | -- Create a row in status_log to reflect the status change of agents, 8 | -- make use of the special variable TG_OP to work out the operation. 9 | -- 10 | IF (TG_OP = 'UPDATE') THEN 11 | IF OLD.status <> NEW.status THEN 12 | INSERT INTO status_log SELECT nextval('status_log_id_seq'), OLD.name, OLD.status, NEW.status, now(); 13 | RETURN NEW; 14 | END IF; 15 | ELSIF (TG_OP = 'INSERT') THEN 16 | INSERT INTO status_log SELECT nextval('status_log_id_seq'), OLD.name, 'initial', NEW.status, now(); 17 | RETURN NEW; 18 | END IF; 19 | RETURN NULL; -- result is ignored since this is an AFTER trigger 20 | END; 21 | $status_change$ LANGUAGE plpgsql; 22 | CREATE TRIGGER status_log_trigger 23 | AFTER INSERT OR UPDATE ON agents 24 | FOR EACH ROW EXECUTE PROCEDURE log_status_change(); 25 | SQL 26 | end 27 | 28 | def down 29 | execute <<-SQL 30 | DROP TRIGGER status_log_trigger on agents; 31 | DROP FUNCTION log_status_change; 32 | SQL 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /tasks/copyright.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | require "pathname" 6 | task :legal do 7 | license = Pathname("LICENSE") 8 | license.open("w+") do |f| 9 | f.puts PROJECT_COPYRIGHT 10 | end unless license.file? and license.read == PROJECT_COPYRIGHT 11 | doc = Pathname("doc/LEGAL") 12 | doc.open("w+") do |f| 13 | f.puts "LICENSE" 14 | end unless doc.file? 15 | end 16 | 17 | desc "add copyright summary to all .rb files in the distribution" 18 | task :copyright => [:legal] do 19 | doc = Pathname("doc/LEGAL") 20 | ignore = doc.readlines. 21 | select { |line| line.strip!; Pathname(line).file? }. 22 | map { |file| Pathname(file).expand_path } 23 | 24 | puts "adding copyright summary to files that don't have it currently" 25 | puts PROJECT_COPYRIGHT_SUMMARY 26 | puts 27 | 28 | (Pathname.glob('{controller,model,app,lib,test,spec,migrations}/**/*{.rb}') + 29 | Pathname.glob("tasks/*.rake") + 30 | Pathname.glob("Rakefile")).each do |file| 31 | next if ignore.include? file.expand_path 32 | lines = file.readlines.map{ |l| l.chomp } 33 | unless lines.first(PROJECT_COPYRIGHT_SUMMARY.size) == PROJECT_COPYRIGHT_SUMMARY 34 | oldlines = file.readlines 35 | file.open("w+") { |f| f.puts PROJECT_COPYRIGHT_SUMMARY + oldlines } 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /tasks/authors.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | # Once git has a fix for the glibc in handling .mailmap and another fix for 6 | # allowing empty mail address to be mapped in .mailmap we won't have to handle 7 | # them manually. 8 | 9 | desc 'Update AUTHORS' 10 | task :authors do 11 | authors = Hash.new(0) 12 | 13 | `git shortlog -nse`.scan(/(\d+)\s(.+)\s<(.*)>$/) do |count, name, email| 14 | # Examples of mappping, replace with your own or comment this out/delete it 15 | case name 16 | when /^(?:bougyman$|TJ Vanderpoel)/ 17 | name, email = "TJ Vanderpoel", "tj@rubyists.com" 18 | when /^(?:manveru$|Michael Fellinger)/ 19 | name, email = "Michael Fellinger", "mf@rubyists.com" 20 | when /^(?:deathsyn$|Kevin Berry)/ 21 | name, email = "Kevin Berry", "kb@rubyists.com" 22 | when /^(?:(?:jayson|thedonvaughn|jvaughn)$|Jayson Vaughn)/ 23 | name, email = "Jayson Vaughn", "jv@rubyists.com" 24 | end 25 | 26 | authors[[name, email]] += count.to_i 27 | end 28 | 29 | File.open('AUTHORS', 'w+') do |io| 30 | io.puts "Following persons have contributed to #{GEMSPEC.name}." 31 | io.puts '(Sorted by number of submitted patches, then alphabetically)' 32 | io.puts '' 33 | authors.sort_by{|(n,e),c| [-c, n.downcase] }.each do |(name, email), count| 34 | io.puts("%6d %s <%s>" % [count, name, email]) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/tiny_call_center/view/queue.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | 5 | require_relative '../../helper' 6 | require "fsr/model/queue" 7 | 8 | describe 'TinyCallCenter Queue' do 9 | behaves_like :rack_test, :make_account 10 | 11 | it "Shows A list of Queues" do 12 | # TODO Load an innate node, set @queues to an array of FSR::Model::Queue instances 13 | # and then test the view output 14 | # Example queue instance: 15 | headers = ["name", "strategy", "moh_sound", "time_base_score", "tier_rules_apply", 16 | "tier_rule_wait_second", "tier_rule_wait_multiply_level", 17 | "tier_rule_no_agent_no_wait", "discard_abandoned_after", 18 | "abandoned_resume_allowed", "max_wait_time", "max_wait_time_with_no_agent", 19 | "record_template"] 20 | data = ["helpdesk@default", "longest-idle-agent", "local_stream://moh", "system", 21 | "false", "30", "true", "true", "60", "false", "0", "0", 22 | "/home/freeswitch/recordings/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"] 23 | queue = FSR::Model::Queue.new(headers, *data) 24 | 25 | TinyCallCenter::Queues.trait queues: [queue] 26 | 27 | res = get('/queues/') 28 | doc = Nokogiri::HTML(res.body) 29 | (doc/:td).map(&:text).should == ["Name", "Strategy", "helpdesk@default", "longest-idle-agent"] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /view/fxc/user/index.xhtml: -------------------------------------------------------------------------------- 1 |

#{l 'Users'}

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
#{l 'Name'}#{l 'Extension'}#{l 'Voicemail'}#{l 'Active'}
#{h user.fullname}#{h user.extension}#{h user.mailbox}#{h user.active}#{a l('Details'), :view, user.id}#{a l('Delete'), :delete, user.id}
37 | -------------------------------------------------------------------------------- /view/agents/index.xhtml: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
#{l 'Name'}
5 |
#{l 'Extension'}
6 |
#{l 'Status'}
7 |
#{l 'State'}
8 |
#{l 'Action'}
9 |
#{l 'Meta'}
10 |
11 | 12 |
13 |
#{TCC::Account.full_name(agent.name)}
14 |
#{agent.extension}
15 |
16 | 21 |
22 |
#{agent.state}
23 |
24 | 25 | 26 |
27 | #{l 'Edit' } 28 |
29 | 30 |
31 |
32 | 33 | 39 | -------------------------------------------------------------------------------- /tasks/release.rake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008-2009 The Rubyists, LLC (effortless systems) 2 | # Distributed under the terms of the MIT license. 3 | # The full text can be found in the LICENSE file included with this software 4 | # 5 | namespace :release do 6 | task :all => [:release_github, :release_rubyforge] 7 | 8 | desc 'Display instructions to release on github' 9 | task :github => [:reversion, :gemspec] do 10 | name, version = GEMSPEC.name, GEMSPEC.version 11 | 12 | puts < [:reversion, :gemspec, :package] do 30 | name, version = GEMSPEC.name, GEMSPEC.version 31 | 32 | puts < NEW.callstate THEN 14 | PERFORM pg_notify('call_update', row_to_json(NEW)::text); 15 | RETURN NEW; 16 | END IF; 17 | ELSIF (TG_OP = 'DELETE') THEN 18 | PERFORM pg_notify('call_delete', row_to_json(OLD)::text); 19 | RETURN OLD; 20 | END IF; 21 | RETURN NULL; -- result is ignored since this is an AFTER trigger 22 | END; 23 | $detailed_calls$ LANGUAGE plpgsql; 24 | SQL 25 | execute <<-SQL 26 | CREATE TRIGGER call_insert 27 | AFTER INSERT ON calls 28 | FOR EACH ROW 29 | EXECUTE PROCEDURE call_notifications(); 30 | CREATE TRIGGER call_update 31 | AFTER UPDATE ON calls 32 | FOR EACH ROW 33 | EXECUTE PROCEDURE call_notifications(); 34 | CREATE TRIGGER call_delete 35 | BEFORE DELETE ON calls 36 | FOR EACH ROW 37 | EXECUTE PROCEDURE call_notifications(); 38 | SQL 39 | end 40 | 41 | def down 42 | execute 'DROP TRIGGER "call_insert" on calls;' 43 | execute 'DROP TRIGGER "call_update" on calls;' 44 | execute 'DROP TRIGGER "call_delete" on calls;' 45 | execute 'DROP FUNCTION call_notifications();' 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/tiny_call_center/fs_listener.rb: -------------------------------------------------------------------------------- 1 | require 'fsr/listener/inbound' 2 | FSR.load_all_commands 3 | 4 | module TCC 5 | class FSListener < FSR::Listener::Inbound 6 | LISTENERS = {} 7 | 8 | def self.log(msg, level = :devel) 9 | Log4r::NDC.push("FSR") 10 | Log.__send__(level, msg) 11 | Log4r::NDC.pop 12 | end 13 | 14 | def log(msg, level = :devel) 15 | self.class.log(msg, level) 16 | end 17 | 18 | def self.execute(reg_server, &block) 19 | log execute: reg_server 20 | 21 | if listener = LISTENERS[reg_server] 22 | listener.execute(&block) 23 | else 24 | create(reg_server){|l| l.execute(&block) } 25 | end 26 | end 27 | 28 | def self.create(server, &block) 29 | log create: server 30 | 31 | EventMachine.connect(server, 8021, self, host: server, port: 8021, auth: 'ClueCon', &block) 32 | end 33 | 34 | def receive_data(data) 35 | 36 | end 37 | 38 | def initialize(*args) 39 | super 40 | @cc_cmd = FSR::Cmd::CallCenter.new(nil, :agent) 41 | @cc_queue = EM::Queue.new 42 | @execute_queue = EM::Queue.new 43 | LISTENERS[@host] = self 44 | end 45 | 46 | def before_session 47 | EM.defer{ execution } 48 | end 49 | 50 | def callcenter!(&given_block) 51 | @cc_queue.push given_block 52 | 53 | @cc_queue.pop do |block| 54 | block.call(@cc_cmd) 55 | log @cc_cmd.raw, :info 56 | api(@cc_cmd.raw) 57 | end 58 | end 59 | 60 | def execute(&block) 61 | @execute_queue.push(block) 62 | end 63 | 64 | private 65 | 66 | def execution 67 | @execute_queue.pop do |block| 68 | block.call(self) 69 | EM.defer{ execution } 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /coffee/tiers.coffee: -------------------------------------------------------------------------------- 1 | p = (obj) -> 2 | window.console?.debug?(obj) 3 | 4 | $ -> 5 | root = $('.tiers_agents .tiers') 6 | root.each (i, tiers) -> 7 | select = $('select[name=status]', tiers).first() 8 | options = [] 9 | $('option', select).each (i,o) -> 10 | options.push($(o).val()) 11 | option_classes = [] 12 | status_classes = {} 13 | for option in options 14 | klass = option.toLowerCase(). 15 | replace(/\W+/g, "-"). 16 | replace(/^-+|-+$/, "") 17 | status_classes[option] = klass 18 | option_classes.push(klass) 19 | control = $('.mass-control', tiers) 20 | control.append(select.clone()) 21 | 22 | newSelect = $('select[name=status]', control) 23 | newSelect.prepend( 24 | $('