poshbotio / poshbot Goto Github PK
View Code? Open in Web Editor NEWPowershell-based bot framework
License: MIT License
Powershell-based bot framework
License: MIT License
It appears we can currently specify one CommandName
in a PoshBot attribute. It might be helpful to allow specifying an array, to allow some alias-esque system, or any other means to allow an author to map multiple poshbot command names to a single actual command.
Example: I might have Get-ADUser as the CommandName
, with a shortcut gu
or qu
that would end up invoking the same PowerShell command under the hood. Some folks don't like to type : )
Cheers!
This project's documentation is somewhat limited and spread across a few markdown files and the wiki. I suggest you migrate towards a documentation solution using either MkDocs or GitBook.
Users should be able to access a documentation site for the project which includes tutorials, design explanations/concept guides, and reference materials. Preferably, this documentation should be versioned, available as a downloadable artifact, source controlled, and built automatically via CI.
Documentation exists in limited form as both markdown documents in the main repository as well as wiki articles in the project.
We could use either MkDocs or GitBook to create the documentation site. GitBook can also be used to export to PDF/epub/mobi format for artifact download.
I suggest breaking documentation into three broad parts:
I have found that this type of documentation makes using a project much easier from both a normal user standpoint as well as from a contributing developer standpoint. It doesn't have to be written all at once but providing a base level of useful documentation and then iterating on it can help to drive adoption and help answer questions about the project more easily.
what went wrong?
$backend = New-PoshBotSlackBackend -Configuration @{Name = 'SlackBackend'; Token =
'SLACK-API-TOKEN'}
$pbc = New-PoshBotConfiguration -BackendConfiguration $backend
New-PoshBotConfiguration : Cannot process argument transformation on parameter 'BackendConfiguration'. Cannot convert
the "SlackBackend" value of type "SlackBackend" to type "System.Collections.Hashtable".
I tend to get those 'xxx values of type xxx' way too often
It might be worth considering functionality to restrict bot, plugin, or command functionality by channel.
Hypothetical scenario: You provide a command that retrieves potentially sensitive information. You have certain channels with team members (or guest accounts) who should not see this information. Currently, a valid user may invite poshbot, and run a command that exposes this information in one of those channels.
If controls were in place, you could solve this various ways:
These could likely be configured in a similar way to user access for the latter two, and via poshbot configs themselves for the bot-level controls.
Not something we personally need at the moment, just thinking of scenarios that might come up down the road
Users should be able to run a command to update one or more plugins used by the bot.
Users should have access to a builtin command, Update-Plugin
, which can update one plugin, several plugins, or all plugins to their latest versions and reload those plugins after the update. It should be able to update to the latest locally available version as well as search remote PowerShell repositories for newer versions.
This functionality does not exist.
Create the new function in the Builtin plugin.
Very often will people want to update existing plugins used by the bot; this should be easy and obvious. This is even more useful for people contributing/developing since their versions will likely change more rapidly.
1.0
We use Mattermost (an open source slack alternative) in our company and since it tries to be API-compatible with slack the work on the backend should be manageable.
Can we have a chat on Gitter/slack to discuss the technical implementation of the current slack backend?
Thanks in advance ;-)
The plugin (module) is installed without error
When running !install-plugin --name <pluginname>
, the command throws the exception below. This appears to be an innocuous error as the plugin is installed fine. Running !get-plugin --plugin <pluginname>
shows the newly installed plugin and executing the plugin commands work correctly.
System.Management.Automation.StopUpstreamCommandsException: System error.
at Microsoft.PowerShell.Commands.SelectObjectCommand.ProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
Run the following command to install a plugin (module) from the PSGallery
!install-plugin --name nameit
Bot starts when you run $bot.start()
on NanoServer.
Bot start is interrupted by GetEnumerator error on NanoServer.
PS C:\> $bot.start()
Cannot find an overload for "GetEnumerator" and the argument count: "0".
At line:1 char:1
+ $bot.start()
+ ~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
PS C:\> $bot | Start-PoshBot -Verbose -Debug
VERBOSE: [SlackBackend:LoadUsers] Adding user: U4ZLZE6E9:megamorf
VERBOSE: [SlackBackend:LoadUsers] Adding user: U4ZGBQQDR:poshbotdockertest
VERBOSE: [SlackBackend:LoadUsers] Adding user: USLACKBOT:slackbot
VERBOSE: [SlackBackend:LoadRooms] Adding channel: C5091LHFZ:general
VERBOSE: [SlackBackend:LoadRooms] Adding channel: C4ZGB5HJ7:random
VERBOSE: [Group: AddUser] User [U4ZLZE6E9] is already in [Admin]
DEBUG: [SlackBackend:ReceiveMessage] Received
{"type":"message","channel":"C4ZGB5HJ7","user":"U4ZLZE6E9","text":"!help","ts":"1492244031.722558","source_team":"T4ZGB5E9Z","team":"T4ZGB5E9Z"}
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"): a
Cannot find an overload for "GetEnumerator" and the argument count: "0".
At line:1 char:1
+ $bot | start-poshbot -Verbose -Debug
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
PS C:\> Resolve-Error
writeErrorStream : True
PSMessageDetails :
Exception : Microsoft.PowerShell.Commands.WriteErrorException: Cannot find an overload for "GetEnumerator" and the argument count: "0".
TargetObject :
CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at Start, C:\Program Files\WindowsPowerShell\Modules\poshbot\0.2.1\PoshBot.psm1: line 2522
at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 17
ScriptName :
Line : $bot.start()
PositionMessage : At line:1 char:1
+ $bot.start()
+ ~~~~~~~~~~~~
PSScriptRoot :
PSCommandPath :
InvocationName :
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
00000000000000000000000000000000000000000000000000000000000000000000000000000000
Message : Cannot find an overload for "GetEnumerator" and the argument count: "0".
Data : {}
InnerException :
StackTrace :
HelpLink :
Source :
HResult : -2146233088
None.
@{
AlternateCommandPrefixes = @('poshbot')
PluginRepository = @('PSGallery')
LogDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
Name = 'poshbotdockertest'
BotAdmins = @('megamorf')
ConfigurationDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
BackendConfiguration = @{
Token = 'xoxb-169555840467-vh3I4ex91hIZVBiGqy7Xpxxx'
Name = 'SlackBackend'
}
LogLevel = 'Verbose'
PluginDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
MuteUnknownCommand = $False
ModuleManifestsToLoad = @()
AlternateCommandPrefixSeperators = @(':',',',';')
SendCommandResponseToPrivate = @()
PluginConfiguration = @{
}
CommandPrefix = '!'
}
$pbc = Get-PoshBotConfiguration -Path .\poshbot.psd1
$backend = New-PoshBotSlackBackend -Configuration $pbc.BackendConfiguration
$bot = New-PoshBotInstance -Configuration $pbc -Backend $backend
$bot.start()
The issue prevents me from testing PoshBot on NanoServer.
PS C:\> $PSVersionTable
Name Value
---- -----
WSManStackVersion 3.0
PSVersion 5.1.14393.1000
CLRVersion
PSEdition Core
BuildVersion 10.0.14393.1000
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.3
PS C:\> Get-Module
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0.2 Configuration {Add-MetadataConverter, ConsoleColor, ConvertFrom-Metadata, ConvertTo-Me...
Manifest 7.0.1 Json.Net
Manifest 3.1.0.0 Microsoft.PowerShell.Management {Add-Content, Clear-Content, Clear-Item, Clear-ItemProperty...}
Manifest 3.0.0.0 Microsoft.PowerShell.Security {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl, Get-Authenti...
Manifest 3.1.0.0 Microsoft.PowerShell.Utility {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Binary 1.0.0.1 PackageManagement {Find-Package, Find-PackageProvider, Get-Package, Get-PackageProvider...}
Script 0.2.1 poshbot {Get-PoshBot, Get-PoshBotConfiguration, New-PoshBotCardResponse, New-Pos...
Script 0.0 PoshBotAttribute
Script 1.0.0.1 PowerShellGet {Find-Command, Find-DscResource, Find-Module, Find-RoleCapability...}
Script 0.0.17 PSSlack {Find-SlackMessage, Get-PSSlackConfig, Get-SlackChannel, Get-SlackGroup...}
Be able to create poshbot instance with New-PoshBotInstance
function on NanoServer (in docker container)
Throws runtime expection due to a null value in the if ($_.TypeId.ToString() -eq 'PoshBot.BotCommand') {
check.
See detailed error info below
PS C:\> Resolve-Error
PSMessageDetails :
Exception : System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.Interpreter.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.LightLambda.RunVoid1[T0](T0 arg0)
at System.Management.Automation.ScriptBlock.InvokeWithPipeImpl(ScriptBlockClauseToInvoke clauseToInvoke, Boolean createLocalScope, Dictionary`2 functionsToDefine, List`1 variablesToDefine, ErrorHandlingBehavior errorHandlingBehavior, Object
dollarUnder, Object input, Object scriptThis, Pipe outputPipe, InvocationInfo invocationInfo, Object[] args)
at System.Management.Automation.ScriptBlock.<>c__DisplayClass57_0.<InvokeWithPipe>b__0()
at System.Management.Automation.Runspaces.RunspaceBase.RunActionIfNoRunningPipelinesWithThreadCheck(Action action)
at System.Management.Automation.ScriptBlock.InvokeWithPipe(Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Pipe outputPipe, InvocationInfo invocationInfo, Boolean
propagateAllExceptionsToTop, List`1 variablesToDefine, Dictionary`2 functionsToDefine, Object[] args)
at System.Management.Automation.ScriptBlock.InvokeUsingCmdlet(Cmdlet contextCmdlet, Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Object[] args)
at Microsoft.PowerShell.Commands.ForEachObjectCommand.ProcessRecord()
at System.Management.Automation.Cmdlet.DoProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
TargetObject :
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : InvokeMethodOnNull
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2197
at GetCommandMetadata, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2196
at CreatePluginFromModuleManifest, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2088
at LoadBuiltinPlugins, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2230
at Initialize, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 1692
at PluginManager, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 1685
at Initialize, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2438
at Bot, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 2418
at New-PoshBotInstance<Process>, C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1: line 3828
at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 2197
OffsetInLine : 17
HistoryId : -1
ScriptName : C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1
Line : if ($_.TypeId.ToString() -eq 'PoshBot.BotCommand') {
PositionMessage : At C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1:2197 char:17
+ if ($_.TypeId.ToString() -eq 'PoshBot.BotCommand') {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot : C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0
PSCommandPath : C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1
InvocationName :
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
00000000000000000000000000000000000000000000000000000000000000000000000000000000
ErrorRecord : You cannot call a method on a null-valued expression.
WasThrownFromThrowStatement : False
Message : You cannot call a method on a null-valued expression.
Data : {System.Management.Automation.Interpreter.InterpretedFrameInfo}
InnerException :
StackTrace : at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.Interpreter.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.LightLambda.RunVoid1[T0](T0 arg0)
at System.Management.Automation.ScriptBlock.InvokeWithPipeImpl(ScriptBlockClauseToInvoke clauseToInvoke, Boolean createLocalScope, Dictionary`2 functionsToDefine, List`1 variablesToDefine, ErrorHandlingBehavior errorHandlingBehavior,
Object dollarUnder, Object input, Object scriptThis, Pipe outputPipe, InvocationInfo invocationInfo, Object[] args)
at System.Management.Automation.ScriptBlock.<>c__DisplayClass57_0.<InvokeWithPipe>b__0()
at System.Management.Automation.Runspaces.RunspaceBase.RunActionIfNoRunningPipelinesWithThreadCheck(Action action)
at System.Management.Automation.ScriptBlock.InvokeWithPipe(Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Pipe outputPipe, InvocationInfo invocationInfo, Boolean
propagateAllExceptionsToTop, List`1 variablesToDefine, Dictionary`2 functionsToDefine, Object[] args)
at System.Management.Automation.ScriptBlock.InvokeUsingCmdlet(Cmdlet contextCmdlet, Boolean useLocalScope, ErrorHandlingBehavior errorHandlingBehavior, Object dollarUnder, Object input, Object scriptThis, Object[] args)
at Microsoft.PowerShell.Commands.ForEachObjectCommand.ProcessRecord()
at System.Management.Automation.Cmdlet.DoProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
HelpLink :
Source : System.Management.Automation
HResult : -2146233088
None.
New-PoshBotConfiguration
and save to file with Save-PoshBotConfiguration
:@{
AlternateCommandPrefixes = @('poshbot')
PluginRepository = @('PSGallery')
LogDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
Name = 'poshbotdockertest'
BotAdmins = @('megamorf')
ConfigurationDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
BackendConfiguration = @{
Token = 'xoxb-169555840467-vh3I4ex91hIZVBiGqy7Xpxxx'
Name = 'SlackBackend'
}
LogLevel = 'Verbose'
PluginDirectory = 'C:\Users\ContainerAdministrator\.poshbot'
MuteUnknownCommand = $False
ModuleManifestsToLoad = @()
AlternateCommandPrefixSeperators = @(':',',',';')
SendCommandResponseToPrivate = @()
PluginConfiguration = @{
}
CommandPrefix = '!'
}
$pbc = Get-PoshBotConfiguration -Path .\poshbot.psd1
$backend = New-PoshBotSlackBackend -Configuration $pbc.BackendConfiguration
$bot = New-PoshBotInstance -Configuration $pbc -Backend $backend
The issue prevents me from testing PoshBot on NanoServer.
PS C:\> $PSVersionTable
Name Value
---- -----
PSEdition Core
CLRVersion
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
PSVersion 5.1.14393.1000
BuildVersion 10.0.14393.1000
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
PS C:\> get-module
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0.2 Configuration {Add-MetadataConverter, ConsoleColor, ConvertFrom-Metadata, ConvertTo-Metadata...}
Manifest 7.0.1 Json.Net
Manifest 3.1.0.0 Microsoft.PowerShell.Management {Add-Content, Clear-Content, Clear-Item, Clear-ItemProperty...}
Manifest 3.0.0.0 Microsoft.PowerShell.Security {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl, Get-AuthenticodeSignature...}
Manifest 3.1.0.0 Microsoft.PowerShell.Utility {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Manifest 3.0.0.0 Microsoft.WSMan.Management {Connect-WSMan, Disable-WSManCredSSP, Disconnect-WSMan, Enable-WSManCredSSP...}
Binary 1.0.0.1 PackageManagement {Find-Package, Find-PackageProvider, Get-Package, Get-PackageProvider...}
Script 0.2.0 PoshBot {Get-PoshBot, Get-PoshBotConfiguration, New-PoshBotCardResponse, New-PoshBotConfiguration...}
Script 0.0 PoshBotAttribute
Script 1.0.0.1 PowerShellGet {Find-Command, Find-DscResource, Find-Module, Find-RoleCapability...}
Script 0.0.17 PSSlack {Find-SlackMessage, Get-PSSlackConfig, Get-SlackChannel, Get-SlackGroup...}
PS C:\> Get-ComputerInfo
WindowsBuildLabEx : 14393.0.amd64fre.rs1_release.160715-1616
WindowsCurrentVersion : 6.3
WindowsEditionId : ServerDatacenterNano
WindowsInstallationType : Nano Server
WindowsInstallDateFromRegistry : 4/14/2017 10:38:01 PM
WindowsProductId :
WindowsProductName : Windows Server 2016 Datacenter
[output omitted]
When running psake as administrator to create markdown help files, the build script errors out because it never imports the PoshBot module.
When running the CreateMarkdownHelp
task via the build script, it should automatically import the version of PoshBot available in the project root and export the function comment-based help.
The build script errors out.
Ensure that the module is properly loaded in a prior step or load and unload in this task itself.
.\build.ps1 -Task CreateMarkdownHelp
Users should be able to run the build tasks without errors, even if only performing one task.
0.1.0
10.0.14393
, PowerShell 5.1.14393.953
Module | Version |
---|---|
BuildHelpers | 0.0.29 |
Psake | 4.6.0 |
PlatyPS | 0.7.6 |
Pester | 4.0.3 |
PSScriptAnalyzer | 1.11.0 |
Should not error out.
`
{"DataTime":"2017-09-09
18:13:57Z","Class":"SlackBackend","Method":"AddReaction","Severity":"Error","LogLevel":"Info","Message":"Error adding
reaction to message","Data":{"ok":false,"error":"no_item_specified"}}
At line:6 char:9
Start-PoshBot -Configuration $pbc -Verbose -ErrorVariable err
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create a function that should fire upon an event being triggered. Example:
`
function WelcomeUserToRoom {
[PoshBot.BotCommand(
Command = $false,
TriggerType = 'event',
MessageType = 'PinAdded'
)]
[cmdletbinding()]
param(
[parameter(ValueFromRemainingArguments)]
$Dummy
)
Write-Output 'YO YO YO'
}
`
Trigger the function with the event (in the above case, pin a post).
The error will be triggered
Event-based functions does not appear to function.
Server 2012 R2, WMF 5.1
When attempting to run PoshBot on a Windows machine with PowerShell version 5.0.10240.1746, it fails to run, saying that it cannot find the cmdlet Import-PowerShellDataFile. After including a function that provides similar functionality as Import-PowerShellDataFile (mapping a psd1 file to a hashtable), I get a different error after the bot receives a message.
The bot starts after running $bot.Start().
When running the bot, it fails with the following error:
Import-PowerShellDataFile : The term 'Import-PowerShellDataFile' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1:3362 char:25
+ $hash = Import-PowerShellDataFile -Path $item
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Import-PowerShellDataFile:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Get-Member : Cannot validate argument on parameter 'Name'. The argument is null or empty. Provide an argument that is
not null or empty, and then try the command again.
At C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1:3365 char:73
+ ... if ($config | Get-Member -MemberType Property -Name $_) {
+ ~~
+ CategoryInfo : InvalidData: (:) [Get-Member], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetMemberCommand
Configuration is missing [Token] parameter
At C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.2.0\PoshBot.psm1:4894 char:17
+ throw 'Configuration is missing [Token] parameter'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Configuration i...oken] parameter:String) [], RuntimeException
+ FullyQualifiedErrorId : Configuration is missing [Token] parameter
This shows that the module is looking for the cmdlet, which does not exist on this version of windows. I have a machine running the latest update and PowerShell 5.1.14393.953 that has this cmdlet, and runs the same code with no issues.
Upon discussing this with the developer, I edited the PoshBot.psm1 file and added the following "replacement" for the related cmdlet:
function Import-PowerShellDataFile {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()]
[hashtable] $Path
)
return $Path
}
This essentially does the same thing, it converts the $Path provided to a hashtable. After including this, the bot does successfully load and begin listening for Slack messages, however when it receives one, it displays the following error:
Exception calling "HandleMessage" with "1" argument(s): "The given key was not present in the dictionary."
At line:1 char:1
+ & 'C:\Users\aemiller\Scripts\Bot\Bot.ps1'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
As it stands, I cannot use PoshBot with our current Windows image.
It would be nice to have an Event Trigger example in the documentation (Triggers.md) so newcomers like myself can understand how that works.
It would be useful to have a standard way to search the gallery for plugins written with PoshBot in mind and which leverage the roles/permissions/other models used by it.
As a naive user, I should be able to search the PSGallery for PoshBot-related modules. Even better would be a way to use PoshBot to search the gallery for plugins.
I either have to know a project supports PoshBot or take an existing module and modify it for PoshBot (if I want to take advantage of permissions/etc). It is very useful to be able to download, install, and import modules from the gallery to the bot!
Perhaps standardize plugins as starting with PoshBot.
or PoshBot-
, similar to how Mkdocs handles themes on PyPi.
We'd have to rewrite the plugin name parser to strip the prefix so the names aren't so long and silly, and we would still want to be able to use normal modules like we can now.
In the meantime, maybe having a document page which lists PoshBot-compatible modules?
While PoshBot has a small user base now, it'd be nice to be able to know what modules are available which were built with PoshBot in mind and where we can take advantage of default roles/etc.
1.0
10.0.14393
5.1.14393.693
New builtin commands should be added to create permissions and to associate those permissions to existing commands.
These commands do not exist
Create a new permission associated with a plugin
New-Permission --name <pluginname:permissionname> --description 'my description'
Associate a permission with a plugin command
Add-CommandPermission --command <pluginname:commandname> --permission <pluginname:permissionname>
The current plugin and permission state that is saved to local storage will have to be extended to allow these adhoc permissions to be saved permanently. The current logic that loads plugins on startup will also have to be modified to merge in these permissions that aren't included with the module manifest.
There will probably be cases where a 3rd party plugin is installed that does not include permissions, or the permissions included do not satisfy the bot administrator's needs.
It might be worth extending the input parser to allow comma separated array input.
Many of the commands I'm writing follow a pattern of picking a set of common default properties to return, while allowing the user to specify an array of properties (passed to select-object, thus allowing wildcards and other fun)
As is, I'm using string input, no spaces, comma separated, and splitting on comma - not pretty! I see some examples using the remaining arguments for a particular parameter - that's another alternative, but a bit less natural than commas, at least for PowerShell folks (and if folks are using positional params in Slack, remaining args option might be a bit confusing)
It doesn't look like it's possible currently but it would be awesome to have the ability to schedule a command to run at 1 time in the future or at a repeating time in the future. Is this on the roadmap?
It might be helpful to handle rate limits:
Identify spots where rate limits would be more likely to hit, if possible, make these optional, potentially use caching, and filtering to the right
Get-Group
resolves all users in every group. This could
-ResolveUsers
or some such parameter, and only resolving names in those cases, andIdentify rate limit details. If possible, design generic error handling to identify when rate limit is hit, retrying after a backoff if needed
Get-Group
Groups show up...
Get-Group
The remote server returned an error: (429) Too Many Requests.
-ResolveUsers
switch.-ResolveUsers
is not specified, do not resolve users (here and here)-ResolveUsers
is specified, resolve users with a delay between calls. Need to find out what the limit is, even 1 second triggered this for me, which seems absurd?Get-Group
You can do this from Slack or just set up your $bot and run the code (a few debug bits I added included from when I was curious):
$groups = foreach ($key in ($Bot.RoleManager.Groups.Keys | Sort-Object)) {
Write-Verbose -Verbose -Message "Resolving [$Key] with [$($Bot.RoleManager.Groups[$key].Users.Keys.count) users] "
[pscustomobject][ordered]@{
Name = $key
Description = $Bot.RoleManager.Groups[$key].Description
Users = $Bot.RoleManager.Groups[$key].Users.Keys | foreach-object {
Start-Sleep -Seconds 1
Write-Verbose "Resolving [$_]" -Verbose
$Bot.RoleManager.ResolveUserToId($_)
}
Roles = $Bot.RoleManager.Groups[$key].Roles.Keys
}
}
Cheers!
Consider a mechanism for running commands as a different user
It may be helpful to include a mechansim to simplify running as a different user.
Currently, if you need to do something with privilege XYZ, there are a few common methods:
Potential alternative:
Provide some convention or handling for credentials, including an option to use them when starting a job.
Hypothetical, poorly-thought-out example:
Ahead of time, as the account running PoshBot:
# Various implementation details to consider. if ^\w$, assume relative path to configuration folder? etc.
New-PoshBotConfiguration ... -Credential @{
AD = '\\Path\To\Serialized\ADCredential'
RT = '\\Path\To\Serialized\RTCredential'
}
Plugins and/or individual commands could be decorated to specify a credential to run with:
[PoshBot.BotCommand(
CommandName = 'disableuser',
Permissions = 'write',
Credential = 'AD'
)]
The last part is tricky. Depending on how PoshBot executes commands (iirc the plan was to use jobs for now?), PoshBot could check the command or plugin config, and if a credential is specified, read the poshbot configuration, deserialize the credential, and use it to launch a job.
Whew!
Does something like this make sense? I don't see it needed in the short term, I'm currently just relying on deserializing credentials with the poshbot plugin / commands themselves, biggest concern is that this makes it a bit tough to share them, and that not all commands offer an easy way to specify credentials (in which case I would need to spawn a job, use a JEA endpoint, or some other workaround)
Cheers!
It appears command history rolls over. It might be helpful to enable this, and perhaps default to this behavior.
Even if this data can be parsed out of the PoshBot logs, might be cleaner, easier to read, and helpful to folks using chat
Just getting the idea out there - happy to help, but haven't put much thought into design
Cheers!
Parameter names are consistent
Parameter names in the builtin commands are not consistent with each other.
For instance, Install-Plugin
uses -Plugin
to reference the plugin to install and Get-Role
using -Role
to reference the role to get. In both cases they should use -Name
which is the PowerShell convention.
Rename all parameters in the builtin commands to use a common naming convention.
A common naming convention will help with usability as most PowerShell users are familiar with the naming convention for parameters.
When trying to run Psake build locally, it errors out during the init task due to not having admin rights. It may also error out when resolving modules in the build.ps1
if the specified modules are not the latest available on the gallery.
Psake tasks should be able to run with normal user privileges and gracefully alert / give option to install if module versions are outdated instead of just updating them globally.
Psake will attempt to upgrade/install modules globally and will fail if not running with administrator privileges.
If we intend to keep the global-update behavior, the scripts should be updated to include a #requires
statement for running as administrator. Otherwise, it's probably preferable to update the CI to offer the option to upgrade globally, install locally, or continue with older versions of the modules.
.\build.ps1 -Task Init -Verbose
Most users, most of the time, will be running the module build process from ISE/VSCode terminal or normal powershell console, and mostly not as administrator. This means they're likely to fail to build locally for reasons that aren't immediately clear; it also means that all build attempts for the project must be executed as administrator which seems unusual.
It also means that the build process is updating the global state of the node the build is running on - not so bad if it's a disposable runner, less fun for a personal workstation, especially if the user did not parse the build script first.
0.1.0
10.0.14393
, PowerShell 5.1.14393.953
When sending a command where the first character of a parameter is "0" i would it expect it to be treated like any other command.
Currently, if a Zero is passed in as the first character of a command, the zero is truncated.
N/A
Work around is to use quotes, which is fine. However, trying to make this as easy as possible not having to include quotes would be ideal.
function Echo
{
param(
$echo
)
Write-output $echo
}
2.Test a test !echo 01821
3.Results are just 1821
i have a command that goes to get config files of products we have. By some devices start with a zero there for it errors out when getting those devices.
Also have a weather and forecast plugin where the truncated zerp would results in cities in other parts of the world.
Poshbot 7.1 -latest
Windows server 2016
Powershell 5.1
Getting up and running with PoshBot interactively is quite straightforward, and covered by existing docs. Some folks may find it helpful to have a short doc explaining how you could use a tool like nssm to service-ize PoshBot.
Side note: Docker is a helpful alternative, but not everyone will have that option
Again, happy to take a stab at this if you think it would be worthwhile!
Not sure if this makes sense…. but have you considered something like Get-PoshBotStatefulData
and Set-PoshBotStatefulData
?
This would allow workflows like… prompting for more input, prompting for confirmation, and any other stateful needs, without junking up the user’s environment. I think.
You might include a Scope
parameter, or some other privacy filter - some authors may write data that only their plugin should have access to. In other cases, you might want global-to-poshbot-instance data that plugins could share to improve integration.
You might serialize to disk on Set
, to ensure PoshBot could pick up state if a bot is restarted (complication: multiple instances? could work around this, but might get funky). While this might help the user, you might also include a DoNotSerialize
parameter if an author is recording sensitive data (for better or worse....)
Cheers!
Will be using BetterCredential to store creds. I'm using a CI pipeline to stand up PoshBot and I am able to store creds through Gitlab. I just need to be able to store them for remote execution for general Noc and Ops tasks.
Be able to store credential in some fashion.
N/A
Create a Credential module or Vault plugin or Import or partner with another module or recommendation.
Trying to create a ops, noc, and dev bot to deploy through choco, to run puppet, and to reboot servers. Need to be able to store creds for multiple domains for basic functions.
Per help New-PoshBotConfiguration -Full
, this is the example found on the -PluginConfiguration
parameter:
@{
Demo = @{
MyParam = 'bar'
}
}
[...snip...]
function Get-Foo {
[cmdletbinding()]
param(
[PoshBot.FromConfig()]
[parameter(mandatory)]
[string]$MyParam
)
Write-Output $MyParam
}
I expect $MyParam to default to 'bar'
.
Importing my custom plugin throws the following error:
❗️ Cannot find an overload for ".ctor" and the argument count: "0".
Debugging the code, I find this is actually happening here:
PoshBot/PoshBot/Classes/PluginManager.ps1
Line 432 in 94931fe
The Get-Help
fails on my Get-Foo
equivalent command while trying to import my custom plugin. After adding help, I found it's actually just choking on the [PoshBot.FromConfig()]
line. I can remove that and it returns help as expected.
I believe this also happens on the included Demo plugin packaged with PoshBot.
Does !ip Demo
work for anyone else right now?
During new plugin development, I'm hoping to store some more...ahem...unsavory data, but I've found I'm not sure how to use the PluginConfiguration
area and the PoshBot.FromConfig
class in tandem.
I found the aforementioned example on the cmdlet-based help, but didn't see anything else on the ReadTheDocs site at this time.
If docs need to be updated, I'm happy to help there once this gets sorted out.
In the parameters -> type section, the color "Greed" should be "Green".
Logging is not implemented fully (or at all) in every class that needs it.
All PowerShell classes that implement any kind of logic in methods should support logging for troubleshooting / auditing purposes.
Derive the classes that need logging from a base class that implements it.
Classes like CommandExecutor
implement a ton of logic that may fail and need troubleshooting or need to be saved to a persistent medium for auditing purposes. Right now, logging is hit or miss.
It seems $env:SLACK_TOKEN is not needed.
None
$env:SLACK_TOKEN is mentioned in the 'Quickstart' section of README.MD and in the comments of 'test.ps1'
*Remove the 'Quickstart' ref
*delete 'test.ps1'
*if any, move missing relevant comments to 'Quickstart' and or help/docs
When using a command that is based off Regex I would expect that only the command being executed at the time would be affected.
I have a command based on Regex that listens for URL's. When a URL is seen in the chat the bot does something with it (shrinks URL's). However, if using another command after that the bot tries to shrink URL's still..
EXAMPLE:
Tadcrazio [6:14 PM]
www.google.com
PoshBot [6:14 PM]
https://goo.gl/fbsS ------This is Expected. 👍
Tadcrazio [6:14 PM]
!insult @tadcrazio
PoshBot [6:14 PM]
https://goo.gl/f54Uhi ----- Not Expected 👎
Tadcrazio [6:15 PM]
!remove-plugin URL ----- In order to correct it, removed the Plugin with the regex.
PoshBot [6:15 PM]
Plugin Removed
Tadcrazio [6:15 PM]
!insult @tadcrazio
Homer SimpsonAPP [6:15 PM]
When you were born, did they let your Mother out of her cell?
Leave it up to you, Unless somehow my Regex pattern is affecting it.
Trying to accomplish the bot automatically picking up long URL's in Slack and shortening them. It can clean up the chat a bit and allow insights into where and when the URL's are being viewed with Google Analytics.
Latest 7.1 from github
Server 2016
Powershell 5.1
This project has a changelog but it is empty.
Users and developers should be able to review a changelog for this project which adheres to the keepachangelog format and thus meets the following criteria:
- It’s made for humans, not machines, so legibility is crucial.
- Easy to link to any section (hence Markdown over plain text).
- One sub-section per version.
- List releases in reverse-chronological order (newest on top).
- Write all dates in
YYYY-MM-DD
format. (Example:2012-06-02
forJune 2nd, 2012
.) It’s international, sensible, and language-independent.- Explicitly mention whether the project follows [Semantic Versioning][https://semver.org].
- Each version should:
- List its release date in the above format.
- Group changes to describe their impact on the project, as follows:
Added
for new features.Changed
for changes in existing functionality.Deprecated
for once-stable features removed in upcoming releases.Removed
for deprecated features removed in this release.Fixed
for any bug fixes.Security
to invite users to upgrade in case of vulnerabilities.
The project has an empty changelog file.
Use the keepachangelog format and ensure that it is updated for each new version/release.
Keeping a standardized changelog makes it easy for people to see and understand what has happened version to version and when those versions were released.
Modules are installed without error.
When installing a module from the PSGallery that itself has a dependency on PoshBot. PowerShell will throw the error below:
The following commands are already available on this system:'Export-Metadata'. This module 'Configuration' may override the existing commands. If you still want to install this module 'Configuration', use -AllowClobber parameter.
This is probably because PoshBot has a dependency on Configuration
so it is already loaded in the running instance of PoshBot. When PoshBot attempts to install a module that depends on PoshBot, OneGet attempts to install the dependency Configuration
and throws this error. Since Configuration is already installed I'm not quite sure why it's trying to install it again.
Add -AllowClobber
switch to Install-Module
cmdlet in the builtin command Install-Plugin
.
Install the PoshBot.Wolfram
module from the PSGallery with
!install-plugin --name poshbot.wolfram
This is a pretty critical bug as any module on PSGallery that depends on PoshBot can't be installed.
Get-Group output can miss data if an array is long enough.
Get-Group
Name : sysadmin
Description : Read and write access
Users : {a, b, c, d, e, f, g}
Roles : {1, 2, 3, 4, 5, 6}
Get-Group
Name : sysadmin
Description : Read and write access
Users : {a, b, c, d...}
Roles : {1, 2, 3, 4...}
$FormatEnumerationLimit = -1
in the PoshBot environment. Generally, elllipsis are bad, if you have no recourse to further explore objects (i.e. with raw PowerShell), it seems like forcing this in the environment (perhaps with an option to override via config) would be acceptableHappy to help if you have any preferences in direction : )
Cheers!
I would expect it to parse all commands regardless of the characters in the command.
When passing in a command, in my case a URL that i want to do something with the bot tends fail if the URL contains parentheses.
Sadly, i don't have any solutions. I believe the bug may lay in the commandparser.ps1.
Super simple function i tested with.
function S
{
[cmdletbinding()]
param(
$text
)
Write-output $text
}
I've been trying to find a way for poshbot to automatically shrink URL's. I've tried multiple ways of doing this but kept running into issues and kept chalking it up to the way i was writing my plugins. After finally have a way that works great I ran into more issues and found out it's not with my plugins but with the bot itself.
Using Poshbot 0.7.0 from github
Server 2016
Powershell 5.1
Poshbot currently provides the ability to respond to a user based on something - either in the same channel or via DM, but that workflow appears to require that the user initiate that communication. It would be slick to provide the ability to @ or DM a user without the user initiating that conversation.
Allow poshbot to message a user without the user initiating the conversation.
Does not appear to allow this functionality.
Allow the user to tell poshbot to look for a particular thing to happen (a particular user comes online, a system is available, etc), and DM the user when that condition is met.
Server 2012 R2, WMF5.1.
Correct documentation
Misspelled word in Event Example 2 - "The next work can be anything" - work should be word.
Missing Command = $false in 2nd Event Example
Will update documentation and submit pull request.
It might be helpful to have a simple how to make changes, test/build locally doc
Not super complex, but this delayed me for a bit - every time I had a few spare minutes to work 'oh! gotta figure out how to run the build to actually produce a module we can import.' This might slow things down for contributors or scare off folks who have something to contribute but aren't quite familiar with the build tools.
Happy to take a stab at this if you think it makes sense.
Side note: I'm completely guilty of this myself : )
PoshBot currently supports custom text responses via New-PoshBotTextResponse
and New-PoshBotCardResponse
. These send custom object types back in the output stream so PoshBot can detect them and handle the custom formatting of the response. It would be nice if PoshBot was able to be told to upload a file without the plugin author needing to handle the particulars for the current backend.
New-PoshBotFileUpload
would be given a path to a file and send that information back to PoshBot. When PoshBot processes the response from the command, it would detect this custom response object, read in the file, and send to the backend for upload.
function Do-Stuff {
[cmdletbinding()]
param()
$myObj = [pscustomobject]@{
value1 = 'foo'
value2 = 'bar'
}
$csv = Join-Path -Path $env:TEMP -ChildPath "$((New-Guid).ToString()).csv"
$myObj | Export-Csv -Path $csv -NoTypeInformation
New-PoshBotFileUpload -Path $csv
}
I'm quite comfort with the current implementation.
Down the road, if this grows, it might be fun and potentially helpful for folks with a different risk appetite to do something like... dynamically constrain each runspace that a command runs in. Available functions, variables, etc. For the paranoid.
Might not even be worth the effort, just brainstorming!
When sending a command where the first character of a parameter is "0" i would it expect it to be treated like any other command.
Currently, if a Zero is passed in as the first character of a command, the zero is truncated.
N/A
Work around is to use quotes, which is fine. However, trying to make this as easy as possible not having to include quotes would be ideal.
function Echo
{
param(
$echo
)
Write-output $echo
}
2.Test a test !echo 01821
3.Results are just 1821
i have a command that goes to get config files of products we have. By some devices start with a zero there for it errors out when getting those devices.
Also have a weather and forecast plugin where the truncated zerp would results in cities in other parts of the world.
Poshbot 7.1 -latest
Windows server 2016
Powershell 5.1
I have a question for those of you you have been testing this module. Have you run into any glaring bugs that would cause you to hesitate publishing on the PowerShell Gallery?
Other than some missing documentation (which will be corrected) I feel this is ready to ship as v1.0 but would be curious what others think.
Warning should probably not be triggered.
18:07:13Z","Class":"PluginManager","Method":"MatchCommand","Severity":"Warning","LogLevel":"Info","Message":"Unable to match parsed command [:] to a plugin command","Data":{}}
Annoyance only.
Server 2012 R2, PS version 5.1
Visual indication that the bot is processing a command given to it. Some commands may be long running and it would be good UX to notify the user that the bot received the message and is processing it.
It would also be handy to mark the message as completed or failed as bot commands/responses in a busy channel may be interwoven with normal user messages and it may not be apparent if a command succeeded or not.
There is currently no indication that the bot is running the command the user entered.
PoshBot should be able to import and execute both functions and cmdlets from PowerShell modules.
When importing a plugin (PowerShell module) into PoshBot, exeptions are thrown when importing cmdlets. Functions work fine.
Cannot convert argument "Command", with value: "Resolve-DnsName", for "GetCommandMetadata" to type "System.Management.Automation.FunctionInfo": "Cannot convert the "Resolve-DnsName" value of type "System.Management.Automation.CmdletInfo" to type "System.Management.Automation.FunctionInfo"."
This is because internally, PoshBot retrieves all the commands from the installed module via Microsoft.PowerShell.Core\Get-Command -Module $ModuleName -CommandType @('Cmdlet', 'Function', 'Workflow')
but stores the commands in a FunctionInfo
property of the created PoshBot Command
instance. FunctionInfo
is of type [System.Management.Automation.FunctionInfo]
and exceptions will be thrown if commands of type [System.Management.Automation.CmdletInfo]
are stored there.
To fix this, the PoshBot Command
class should be modified to accept commands of type FunctionInfo
and CmdletInfo
. The PluginManager
and CommandExecutor
classes will also need to be modified to handle the logic for dealing with commands of either type appropriately.
Run the following command in PoshBot to install the DnsClient module. This module includes both functions and cmdlets.
!install-plugin dnsclient
PoshBot should be able to install and execute either functions or cmdlets.
It might be worth adding a watchdog process of sorts. For example:
Not sure if this is feasible (i.e. do all back ends have concept of a private message to yourself?)
Every so often I find the bot disconnects, with the service still running, and nothing apparent in logs - a watcher might help resolve this when it happens
Cheers!
Startup Script: Trying to push this through a CI and would like to set psrepo in some sort of startup script in my New-PoshBotScheduledTask. Also, I had to add are reboot to the CI task to make poshbot come up at the end. It'd be nice if I would be able to start tasks instead of reboot. Also more through documentation of the plugin directories, the *.psd1 files I put there don't seem to be loaded when I specify them in ModuleManifestsToLoad.
new Parameter for StartupScript = "$botdir.poshbot\startup.ps1"
null
I created a new GitHub organization PoshBotIO to hold all the related PoshBot projects. I intend to move this project into that organization in the next few days. While this shouldn't cause any problems, I figured I'd raise this issue to notify everyone.
https://help.github.com/articles/about-repository-transfers/
After I transfer the project, your git remote
links should still work and redirect to the new location but to avoid any confusion, it is strongly recommended to run:
git remote set-url origin <new_url>
I'll let this issue simmer for a few days before I move the project. Once I do, I'll respond to this issue with the new URL.
PoshBot should be able to run in the background
No function exists. The user is left to their own devices to get PoshBot running in the background.
Add new function to create a scheduled task that runs a small entry point script to load the PoshBot configuration file and start PoshBot.
Allow PoshBot to be running in the background and ensure it starts after a restart.
Let's pretend we have a plugin with 3 commands:
Group
GroupMember
Groups
It is difficult to get help for Group
!help Group
Detailed help for Group appears
!help Group
Everything with group in it comes back. Even if I fully specify the fully qualified names
!help Group -detailed
Everything with group in it comes back. Even if I fully specify the fully qualified names
I do like the fuzzy-ish search to simplify usage. That said, from my perspective, if a commandname has an exact match, only that item should be returned. Perhaps move to:
Completely open to alternatives though (e.g. another parameter that doesn't do fuzzy matching)
Getting help for commands with generic names that are found elsewhere is difficult if not impossible (I might be overlooking something).
We currently list out example uses on a doc page, but down the line I would expect bot !help usage to become the preferred option.
Cheers!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.