Alpha: Craft Your Own Web-based Chatbot using Javascript

Craft your own (fully customizable) web-based chatbot

Check out our live demo

Real life implementation as Icalia Labs Virtual Sales Assisstant here

Alpha is a bot, or rather a base to craft your own web-based chatbot. We started this project because we think chatbots can be super helpful and quite fun, however the current state of affairs has most chatbots limited to existing messaging platforms such as Facebook, which is quite OK, but what if you want your bot to live elsewhere on the web?

You can build your own bot and define your own visual styles and rules as well as have unlimited control for customization and logic. Various solutions already exist, but they can be limiting, costly and hard to implement.

This is where Alpha comes handy. This library is designed to let you build your own bot in a very simple way. You just have to:

  1. Download
  2. Insert your own Q&A tree
  3. Insert your own colors and images

Alpha takes care of the whole UI rendering and handling of the application state. You don't really need to know any React (or Javascript for that matter). However, if you know your way around React and Redux you can further customize the rendering of the bot-app, and even connect it to any back-end or AI engine of your choice.

Installation

Alpha is built to run on Docker. However you can run it locally as long as you have NodeJS as well as npm or yarn (preferred!) in your system.

Development

To install and run in your computer just run the following commands in your terminal. You'll need to have Docker installed (See below for instructions without docker):

  1. Clone this repo: git clone https://github.com/IcaliaLabs/alpha.git
  2. Navigate into directory: cd alpha
  3. Install dependencies: docker-compose run --rm alpha yarn install
  4. Then you can lift the server: docker-compose up alpha

That's it! you should see the demo bot up and running in your browser if you open localhost:3000* in your browser.

A couple of important points when running on development mode:

  • The bot app will have Hot Reloading enabled, so that you can experience the benefits of Webpack + React.
  • Redux DevTools are enabled by default so that you can inspect the app's actions/state chages in real time.
  • The time between each bot message is set to 0. This is to minimize developers' frustration when they go over the conversation flow again and again, however you can change this behavior if you wish at app/containers/BotContainer/sagas.js.

Production

Heroku: Just run git push heroku master. This bot is Heroku-ready!

Local with Docker: If you can to compile the image to see how it will behave on production, we put a build together as well. Just run:

  docker-compose up release

It will build the image and lift the production server.

Without Docker

If you wish to install and run without Docker you'll have to install all the dependencies directly in your directory:

For Development:

  1. Clone this repo: git clone https://github.com/IcaliaLabs/alpha.git
  2. Navigate into directory: cd alpha
  3. Run either npm install or yarn install to install the dependencies
  4. Run npm start to fire up the server
  5. Visit localhost:3000 in your browser

For Production:

  • This repo is Heroku ready, just do git push heroku master
  • If for some reason you alter your package.json's scripts, be sure to consider these steps before deploying to a production server.
  • For AWS you can follow the same steps listed here
  • For Azure/Softlayer/other servers you'll have to ssh to the server, pull the repo, install the dependencies using yarn and then do npm run build in order to create the ./build folder. Finally you can start the server using start:prod. Alternatively just run start:production and it'll run these steps together in sequence, plus tests.

Customizing the Dialogue

All the logic behind what the bot says, including the Q&A logic live inside the BotMind which lives at app/BotMind/. We call each individual bot or user input as Bubbles because they are renderd as such in the chat UI.

In order to customize the dialogue you just need to understand and edit app/BotMind/_initialBubble.js and app/BotMind/BotMindFlows/index.js.

The BotMind

The main file for the BotMind is BotMind.js, but this file only acts as the collector and exporter of the functions living at _initialBubble.js, _nextBubble.js, and _recommendationBubbles.js. These three files in turn carry out their logic based on the Q&A trees that live inside app/BotMind/BotMindFlows/.

_initialBubble.js

Here you can set at which point in the conversation tree does your bot start when initialized or when the user selects to restart the conversation.

_nextBubble.js

This file contains the logic of which bubble or dialogue should the bot jump to based on the last message from the bot or from the user. You don't really need to alter this file, unless you wish to dig into much finer customization. Proceed at your own risk.

_recommendationBubbles.js

This file carries out the logic of choosing a path when shouldEstimateRecommendation: true is passed in a dialogue bubble. This file acts based on the points accumulated by the Bags System during the conversation. You don't really need to alter this file, unless you wish to dig into much finer customization. Proceed at your own risk.

The BotMindFlows

The BotMindFlows live at app/BotMind/BotMindFlows/. By default here you will find just an index.js file, but if your conversation tree starts to get too big you can create your own files and use the index.js to combine them using the spread operator like this.

import greetings from './conversation1';
import designSprint from './conversation2';
import designSprintQuestions from './conversation3';

const questions = {
    ...conversation1,
    ...conversation2,
    ...conversation3,
}

export default questions;

Each time the BotMind analyses where to go (inside _nextBubble.js) it expects to find a hash of questions inside app/BotMind/BotMindFlows/index.js. Questions is made up of... you guessed it, individual question-answer hashes. Here you can start laying down the logic and the flow of the conversation. For instance in our demo you'll find the following:

const questions = {
    start:{
        botPrompt: "Hello Human, my name is <strong>Alpha</strong>, I am an awesome <strong>chatbot</strong>",
        answers: [
            {
                nextId: "myPurpose"
            }
        ]
    },
    myPurpose:{
        botPrompt: "My purpose is to be a simple chatbot that <strong>guides users</strong> and <strong>is able to make decisions and make recommendations.</strong>",
        answers: [
            {
                nextId: "yourName"
            }
        ]
    },
    yourName:{
        botPrompt: "So, What is your name?",
        input: textField(),
        answers: [
          {
            answer: common_greetings,
            nextId: "greetings_notAName",
          },
          {
            answer: common_greetings_negative,
            catchName: true,
            nextId: "asYouCanSee",
          },
        ],
    },
    ...
}
export default questions;

Each question hash must have it's own unique ID. We recommend that you make these IDs as self-explanatory as possible, even if it means writing a little bit more, because as your bot's conversation library grows, remembering which ID does what can become a pain in the A. Every question hash takes up the following options:

  • botPrompt ([String] required): The message from the bot
  • answers ([Array] required): The different responses the bot may have based on what the user answers. The array is made up of hashes, each representing a possible conversation-flow outcome. The hashes inside [answers] take the following options:
    • nextId ([String] required): Determines which dialogue ID the bot will navigate to if this answer is met. On the last message from your bot or before you jump to estimate a recommendation (see below), then you should pass null.
    • answer ([String | RegEx]): The user answer that will trigger this hash. If you are using selectField or tagsField to pre-define answers for the user, you'll be fine using strings, otherwise you may want to use RegEx (see examples).
    • sumToBags ([Array]): here you can start adding points to the BagsSystem in order to make more advanced flow-controls and/or emit recommendations if that's your bot's purpose. sumToBags will usually look like this: sumToBags:[{name: "recommendation1", points: 1}, {name: "recommendation2", points: 3}]
    • catchName ([Boolean]): Will signal React to store the user's name from the users message into the app state.
    • catchCompanyName ([Boolean]): Same as above but for a company name.
    • catchMail ([Boolean]): Same as above but for email addresses.
    • catchPhone ([Boolean]): Same as above but for phone numbers.
    • shouldEstimateRecommendation ([Boolean]): Will signal the BotMind to break the regular flow and analyze the Recommendation Bags in order to move the conversation forward (see examples).
    • finishConversation ([Boolean]): Will signal the bot to finish the conversation on the next dialogue. When this happens user input will disable and the bot will "go offline" until the user selects "Restart".

NOTE – if your botPrompt for this dialogue part does not expect ramification, you can just pass nextId like this :

answers: [
	{
		nextId: "hello"
	}
]
  • input ([Object]): This determines what type of input is available to the user during this botPrompt (text field, tags, select, disabled, etc.). By default inputs are disabled text fields. We recommend that you use the StateFormatter helpers included at the beginning of the index.js file.
  • type ([String]): What kind of message is the bot transmitting ("text", "media", "link", or "transformedText").
  • varName ([String]): If you choose "transformedText" as your input type, you can write @varName in the botPrompt, which will refer to this option. When the bot renders the message it will look for the variable stored in the React state (see below) and replace @varName for the value of that variable.

Making 'Recommendations' to Users (choose dialogue path based on accumulated dialogue history)

As mentioned above, you can choose to store some values in the React state, and later on compute a reaction/flow/recommendation based on these values. We call this the Bags System.

The Bags System

The concept of the bags system is that we pre-define some "bags" which we can fill with "points" as the conversation progresses. Finally when you decide to call shouldEstimateRecommendation: true on one of your question-answer hashes, _recommendationBubbles.js will be called into action to decide what to show next based on how many points each bag has accumulated.

The first step is to define the bags you'll be using at /app/BotMind/recommendationBags.js. If you do not define these bags, the bot will work, but won't know what to do when you call addToBags or shouldEstimateRecommendation. The bags look like this:

const recommendationBags = [
    {
        name: "redWine",
        defaultValue: 0,
        goToBubbleId: "redWine_start",
    },
    {
        name: "whiteWine",
        defaultValue: 0,
        goToBubbleId: "whiteWine_start",
    },
    {
        name: "roseWine",
        defaultValue: 0,
        goToBubbleId: "roseWine_start",
    },
]

export default recommendationBags;

Afterwards you can define when to add using addToBags in your question-answer flows, for instance:

question1:{
    botPrompt: "Which of these are you having?",
    input: selectField(["Red Meat", "Sea Food", "Chicken", "Pasta"]),
    answers: [
            ...
            {
                answer: "Red Meat",
                nextId: "question2",
                sumToBags:[{name: "redWine", points: 4}, {name: "roseWine", points: 1}]
            },
            ...
        ]
    }

And when you're ready just call shouldEstimateRecommendation like this:

question2:{
    botPrompt: "What will you serve for dessert?",
    input: selectField(["Chocolate", ...]),
    answers: [
        ...
        {
            answer: "Chocolate",
            shouldEstimateRecommendation: true,
            nextId: null,
            sumToBags:[{name: "redWine", points: 3}, {name: "whiteWine", points: 1}, {name: "roseWine", points: 2}]
        },
        ...
    ]
},

In this example _recommendationBubbles.js will be called, and thus the next bot bubble will probably be the one at redWine_start.

Customizing the UI

We've taken the liberty to predefine a UI to save you some time.

The vast majority of the Bot's styles are ruled by a single file, /app/customization/styleVariables.js, here you can change pretty much all the colors used for all the elements, as well as the background of the UI.

If you wish to make further changes to the UI, you can dig right into the stylesheet files. There is a global file at /app/global-styles.js and some components or containers have their own styledComponents.js files in their respective folders for particular, modular components. These files use Styled-Components, a pretty awesome library for React/ES6 (which is officialy considered as a best-practices aid). These files use Javascript's tagged template literals to interpolate JS vars with CSS, but don't freak out, these can be treated pretty much as regular CSS/SCSS.

NOTE - Our intention is to gradually migrate to 100% independent, modularly styled components such as the ones found at /app/components/UserInput/styledComponents.js and get rid of pretty much all the code at /app/global-styles.js

If your customization wishes are not satisfied just by fiddling with the CSS of the coponents, you can customize pretty much everything else at your will, but beyond this point you'll have to deal with the React (+Redux) side of the app (see below).

Customizing the React app

The whole React side of Alpha has been refactored taking this awesome repo as a base. Leveraging the most well-established best practices for React, namely the use of:

  • Redux
  • ImmutableJS
  • Reselect
  • Redux-Saga
  • Styled-Components

If you wish to fiddle with the React side of Alpha, then you better take a look at these docs first.

Sending Emails (TBD)

We're sure that you'd like to send automated emails to the Bot's owner and end-users with recap of their conversation or such, and we're working on a way to offer the most flexible and back-end agnostic solution that'll allow to virtually just "Plug & Play".

Right now your best bet is hooking this up to some Node or Express (or Rails 5.1.x) server and leveraging their own mailer solutions. You can take a look at the file /app/BotMind/BotMailer.js which we use for our Rails-based implementation, but as of now we have removed all the send-Email logic from the /app/containers/BotContainer/sagas.js, so you'd have to write your own sagas.

Connecting to any Back-End (TBD)

This bot is based on React + Webpack only, meaning that you should be able to plug it in with any framework, back-end, etc., as long as it works with Webpack.

At the moment the Bot can seamlessly connect with any API, but you'll have to write down your own action creators and sagas to get your desired behavior.

We'll add documentation here along the way as we adapt this bot for different implementations.

Connecting to AI Engines (TBD)

Same as above, we intend to allow this bot to connect to other Artificial Intelligence Engines such as Api.ai and IBM's Watson in order to enhance interactions. This is one of our top to-do's.

Download Details:
Author: IcaliaLabs
Source Code: https://github.com/IcaliaLabs/alpha
License: MIT License

#chatbot  #javascript 

What is GEEK

Buddha Community

Alpha: Craft Your Own Web-based Chatbot using Javascript
Chloe  Butler

Chloe Butler

1667425440

Pdf2gerb: Perl Script Converts PDF Files to Gerber format

pdf2gerb

Perl script converts PDF files to Gerber format

Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.

The general workflow is as follows:

  1. Design the PCB using your favorite CAD or drawing software.
  2. Print the top and bottom copper and top silk screen layers to a PDF file.
  3. Run Pdf2Gerb on the PDFs to create Gerber and Excellon files.
  4. Use a Gerber viewer to double-check the output against the original PCB design.
  5. Make adjustments as needed.
  6. Submit the files to a PCB manufacturer.

Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).

See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.


pdf2gerb_cfg.pm

#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;

use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)


##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file

use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call

#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software.  \nGerber files MAY CONTAIN ERRORS.  Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG

use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC

use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)

#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1); 

#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
    .010, -.001,  #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
    .031, -.014,  #used for vias
    .041, -.020,  #smallest non-filled plated hole
    .051, -.025,
    .056, -.029,  #useful for IC pins
    .070, -.033,
    .075, -.040,  #heavier leads
#    .090, -.043,  #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
    .100, -.046,
    .115, -.052,
    .130, -.061,
    .140, -.067,
    .150, -.079,
    .175, -.088,
    .190, -.093,
    .200, -.100,
    .220, -.110,
    .160, -.125,  #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
    .090, -.040,  #want a .090 pad option, but use dummy hole size
    .065, -.040, #.065 x .065 rect pad
    .035, -.040, #.035 x .065 rect pad
#traces:
    .001,  #too thin for real traces; use only for board outlines
    .006,  #minimum real trace width; mainly used for text
    .008,  #mainly used for mid-sized text, not traces
    .010,  #minimum recommended trace width for low-current signals
    .012,
    .015,  #moderate low-voltage current
    .020,  #heavier trace for power, ground (even if a lighter one is adequate)
    .025,
    .030,  #heavy-current traces; be careful with these ones!
    .040,
    .050,
    .060,
    .080,
    .100,
    .120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);

#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size:   parsed PDF diameter:      error:
#  .014                .016                +.002
#  .020                .02267              +.00267
#  .025                .026                +.001
#  .029                .03167              +.00267
#  .033                .036                +.003
#  .040                .04267              +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
    HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
    RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
    SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
    RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
    TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
    REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};

#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
    CIRCLE_ADJUST_MINX => 0,
    CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
    CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
    CIRCLE_ADJUST_MAXY => 0,
    SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
    WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
    RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};

#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches

#line join/cap styles:
use constant
{
    CAP_NONE => 0, #butt (none); line is exact length
    CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
    CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
    CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
    
#number of elements in each shape type:
use constant
{
    RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
    LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
    CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
    CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
    rect => RECT_SHAPELEN,
    line => LINE_SHAPELEN,
    curve => CURVE_SHAPELEN,
    circle => CIRCLE_SHAPELEN,
);

#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions

# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?

#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes. 
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes

#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches

# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)

# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time

# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const

use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool

my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time

print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load


#############################################################################################
#junk/experiment:

#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html

#my $caller = "pdf2gerb::";

#sub cfg
#{
#    my $proto = shift;
#    my $class = ref($proto) || $proto;
#    my $settings =
#    {
#        $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
#    };
#    bless($settings, $class);
#    return $settings;
#}

#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;

#print STDERR "read cfg file\n";

#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names

#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }

Download Details:

Author: swannman
Source Code: https://github.com/swannman/pdf2gerb

License: GPL-3.0 license

#perl 

Erwin  Boyer

Erwin Boyer

1624502703

11 Of The Best Artificial Intelligence Enterprise Chatbots in 2021

Chatbots for businesses help them engage their website visitors and convert them into potential customers. The implementation of chatbots transforms the way businesses interact with their users. They can use a chatbot AI for sales, marketing, customer support, and automate many other business tasks.

The AI chatbots have revolutionized the customer service experience and enabled businesses to serve their customers in a better way. Chatbots, if created and used right, can help you take your business to all-new levels of success.

To make the best AI chatbot for your business, you need an efficient chatbot builder with various advanced features. In this post, we have listed different chatbot builders with their features, pros, and cons. Just go through the post and find the one that best fits your business needs.

chatbot for your business.

  1. Chatfuel
  2. Gupshup
  3. Appy Pie Chatbot
  4. ChatterOn
  5. MobileMonkey
  6. ActiveChat.ai
  7. Imperson
  8. SnatchBot
  9. Botsify
  10. BotCore
  11. Pandorabots

#chatbots #chatbot-development #ai-chatbot #customer-support-chatbots #power-of-chatbots #enterprise-chatbots #use-cases-of-chatbots #what-is-a-chatbot

Juned Ghanchi

1621315103

Chatbot Service India, Chatbot Development Company India

We provide modernistic chatbot app development services in India and across the world. Voice bots and chatbots created by our team of developers will transform and channelize your communication process with the clients.

Using chatbot apps for business development is a trend. Our developers build apps using the latest technologies like Dialogflow, IBM Watson, Amazon Lex, fastText, Rasa NLU, & Microsoft Bot Framework.

To revolutionize the business development process, hire chatbot app developers in India.

#chatbot service india #chatbot development company india #chatbot developers india #chatbot services #chatbot development company #chatbot developers

Erwin  Boyer

Erwin Boyer

1624498185

AI Chatbots for Business: Why You Need One Now!

It’s said that Artificial Intelligence will be just as smart as humans by 2050. Experts like Ray Kurzweil have even predicted that we’ll achieve a technological singularity by 2045.

From that point on, it’s believed that AI will start inventing Nobel Prize-winning inventions every 5 minutes. Granted it’s gonna be out of our control, but hey, at least we’ll see a revolutionary breakthrough.

We may think that these claims are outlandish and ridiculous, but if someone were to tell me in the 70s that there will be self-driving cars in the future, I would’ve wanted to smoke whatever they were smoking.

But guess what, here we are in 2020, and Tesla already has their self-driving cars on the roads right now. And these were all recently developed technologies. Did you know that the first chatbot was actually launched in 1966?

Features of AI Chatbots

Why Do You Need an AI Chatbot?

Chatbots Across Various Industries

Wrapping Up

#ai-chatbot #what-is-a-chatbot #chatbot-online #chatbot #chatbot-website #facebook-chatbot #google-chatbot #best-chatbot

Erwin  Boyer

Erwin Boyer

1624510080

How This AI-Enabled Chatbot Radically Transformed Cancer Care Amid Pandemic

The critical industry that has been massively impacted by the pandemic is the healthcare sector; however, AI’s involvement has helped the industry weather the pandemic storm. The AI applications by companies bring back patients’ lives from the edge and improve diagnostics and treatment and help healthcare providers make informed decisions quickly. One such application has been developed by Hospido, India’s first holistic cancer care platform with which cancer patients can get the right treatment advice from India’s best cancer doctors, without visiting hospitals amid pandemic.

The pandemic lockdown has forced many people, including cancer patients, to avoid hospitals and discontinue their treatment due to coronavirus risk. And this is what triggered Karan Chopra, the founder of Hospido to bring out quality healthcare to these cancer patients through telemedicine and satellite treatments centres for providing the right treatment at the right time. In this article, Analytics India Magazine, got in touch with him to understand how this startup revolutionised cancer carer amid pandemic.

#startups #ai chatbot #ai chatbot transformed cancer care #ai-enabled chatbot #chatbot #chatbot ai #chatbot for pandemic #chatbot india #chatbot transformed cancer care #hospido startup