ऐप्स (प्लग-इन) लिखना

ऐप्स Antimatter में अंदर से फीचर्स जोड़ते हैं: Go में एक सर्वर हिस्सा जो सर्वर में हुक होता है, और React में एक वेब ऐप हिस्सा जो मेनू, पैनल, मैसेज टाइप्स और पेज जोड़ता है। यह पेज एक ऐप का लेआउट कैसे होता है, यह दिखाता है, जिसमें बंडल्ड Polls ऐप का उपयोग उदाहरण के रूप में किया गया है, और Antimatter के Fusion इंटरफ़ेस में क्या बदलाव आते हैं।

एक ऐप कैसे काम करता है

  • सर्वर हिस्सा एक Go प्रोग्राम है। सर्वर इसे एक अलग प्रक्रिया के रूप में शुरू करता है और RPC के माध्यम से इससे बातचीत करता है: यह ऐप के हुक्स को कॉल करता है (एक मैसेज पोस्ट होने वाला है, एक कमांड चलाया गया है…), और ऐप सर्वर के प्लग-इन API को कॉल करता है (एक पोस्ट बनाएं, एक यूज़र पढ़ें, डेटा स्टोर करें…)। यह /plugins/<app id>/ के तहत HTTP रूट भी सर्व कर सकता है।
  • वेब ऐप हिस्सा एक JavaScript बंडल है जिसे वेब ऐप के साथ ब्राउज़र में लोड किया जाता है। यह रजिस्ट्री के साथ React कंपोनेंट्स और कॉलबैक्स रजिस्टर करता है: एक मेनू में बटन, एक पैनल, या एक मैसेज टाइप को ड्रॉ करने का तरीका।
  • दोनों हिस्से वैकल्पिक हैं। एक ऐप .tar.gz बंडल के रूप में इंस्टॉल किया जाता है, और सिस्टम एडमिन इसे सक्षम, अक्षम और कॉन्फ़िगर करते हैं।
सर्वर हिस्सा सर्वर के डेटा तक पूर्ण एक्सेस के साथ चलता है। ऐप को ऐसे कोड की तरह मानें जिसे आप सर्वर पर डिप्लॉय करते हैं।

Antimatter Mattermost के लिए लिखे गए ऐप्स चलाता है, और इसके विपरीत भी, बशर्ते कि ऐप केवल उन हुक्स का उपयोग करे जो दोनों में उपलब्ध हैं। देखें केवल Antimatter में उपलब्ध हुक्स।

बंडल लेआउट

कोई अलग स्टार्टर टेम्पलेट नहीं है: antimatter-plugin-polls या antimatter-plugin-todo जैसे छोटे बंडल्ड ऐप से शुरू करें, और उसकी ID और कोड बदलें। Polls इस प्रकार लेआउट किया गया है:

antimatter-plugin-polls/
├── plugin.json            the manifest
├── Makefile               build, test, bundle, deploy
├── build/                 the manifest and pluginctl helper tools (Go), setup.mk
├── assets/icon.svg        files shipped with the bundle
├── go.mod
├── server/
│   ├── main.go            plugin.ClientMain(&Plugin{})
│   ├── plugin.go          the Plugin type, OnActivate, ServeHTTP
│   ├── manifest.go        generated from plugin.json
│   ├── api.go, command.go, hooks.go, store.go …
│   └── dist/              compiled binaries, one per platform
└── webapp/
    ├── package.json, webpack.config.js, tsconfig.json
    ├── i18n/              en.json and one file per language
    ├── src/index.tsx      registers the app with the web app
    ├── src/manifest.ts    generated from plugin.json
    ├── src/translations.ts
    └── dist/main.js       the bundle

वैकल्पिक: public/ फोल्डर, जो /plugins/<app id>/public/ पर जैसा है वैसा ही सर्व किया जाता है।

plugin.json

{
  "id": "com.example.weather",
  "name": "Weather",
  "description": "Forecasts in the chat.",
  "homepage_url": "https://github.com/example/weather",
  "support_url": "https://github.com/example/weather/issues",
  "icon_path": "assets/icon.svg",
  "version": "1.0.0",
  "min_server_version": "12.0.0",
  "server": {
    "executables": {
      "linux-amd64": "server/dist/plugin-linux-amd64",
      "linux-arm64": "server/dist/plugin-linux-arm64",
      "darwin-amd64": "server/dist/plugin-darwin-amd64",
      "darwin-arm64": "server/dist/plugin-darwin-arm64",
      "windows-amd64": "server/dist/plugin-windows-amd64.exe"
    }
  },
  "webapp": {"bundle_path": "webapp/dist/main.js"},
  "settings_schema": {"settings": []}
}
  • id: अद्वितीय और स्थायी; एक रिवर्स डोमेन नाम आमतौर पर होता है। यह रूट्स, स्टोर की गई सेटिंग्स और WebSocket इवेंट्स का नाम निर्धारित करता है।
  • version: सेमैंटिक वर्ज़न। बंडल्ड ऐप्स इसे plugin.json से बाहर रखते हैं; उनका बिल्ड टूल इसे नवीनतम v* git टैग से लेता है।
  • min_server_version: Antimatter 12.0.0 रिपोर्ट करता है।
  • settings_schema: सिस्टम कंसोल में ऐप की सेटिंग्स पेज।

make apply मैनिफेस्ट को server/manifest.go और webapp/src/manifest.ts में कॉपी करता है; make manifest-check इसकी सत्यापन करता है।

सर्वर का हिस्सा

सर्वर का हिस्सा Mattermost ऐप्स के समान Go मॉड्यूल, github.com/mattermost/mattermost/server/public, को Go मॉड्यूल प्रॉक्सी से इम्पोर्ट करता है। इसका main केवल ऐप को शुरू करता है:

package main

import "github.com/mattermost/mattermost/server/public/plugin"

func main() {
	plugin.ClientMain(&Plugin{})
}

Plugin टाइप plugin.MattermostPlugin को एम्बेड करता है, जो इसे p.API देता है, और वह हुक्स लागू करता है जिनकी उसे ज़रूरत है:

type Plugin struct {
	plugin.MattermostPlugin
	router *mux.Router
}

func (p *Plugin) OnActivate() error {
	p.router = p.newRouter()
	return p.API.RegisterCommand(&model.Command{
		Trigger:          "weather",
		AutoComplete:     true,
		AutoCompleteDesc: "Show the forecast",
		AutoCompleteHint: "[city]",
	})
}

func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
	city := strings.TrimSpace(strings.TrimPrefix(args.Command, "/weather"))
	return &model.CommandResponse{
		ResponseType: model.CommandResponseTypeEphemeral,
		Text:         "Forecast for " + city + ": sunny",
	}, nil
}

func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
	p.router.ServeHTTP(w, r)
}

सामान्य हुक्स

हुककब चलता है
OnActivate, OnDeactivateजब ऐप सक्षम या निष्क्रिय किया जाता है, और जब सर्वर शुरू और बंद होता है। OnActivate में कमांड्स और बॉट्स को रजिस्टर करें।
OnConfigurationChangeजब इसकी सेटिंग्स बदलती हैं; उन्हें p.API.LoadPluginConfiguration(&cfg) के साथ लोड करें।
ServeHTTP/plugins/<app id>/… के लिए अनुरोध। जब अनुरोध साइन-इन किए गए उपयोगकर्ता से आता है, तो सर्वर Mattermost-User-Id हेडर सेट करता है; यदि वे सार्वजनिक होने के लिए नहीं हैं, तो बिना इसके अनुरोधों को अस्वीकार करें।
ExecuteCommandजब ऐप द्वारा रजिस्टर किया गया कोई कमांड चलाया जाता है।
MessageWillBePosted, MessageWillBeUpdatedसंदेश सहेजे जाने से पहले: एक बदला पोस्ट लौटाएं, या इसे अस्वीकार करने का कारण दें।
MessageHasBeenPosted, MessageHasBeenUpdated, MessageHasBeenDeletedबाद में, साइड इफेक्ट्स के लिए।
ReactionHasBeenAdded, ReactionHasBeenRemovedप्रतिक्रियाएँ।
UserHasJoinedChannel, UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam, ChannelHasBeenCreatedसदस्यता और चैनल परिवर्तन। एक टीम एक समुदाय है।
FileWillBeUploadedफ़ाइल स्टोर किए जाने से पहले: इसे बदलें या अस्वीकार करें।
OnPluginClusterEventक्लस्टर के किसी अन्य सर्वर पर उसी ऐप से आया संदेश।

पूरी सूची, सटीग सिग्नेचर्स के साथ, server/public/plugin/hooks.go में है; API server/public/plugin/api.go में है। pluginapi पैकेज (server/public/pluginapi) API को दोस्ताना हेल्पर्स में रैप करता है।

उपयोगी API कॉल

  • CreatePost, UpdatePost, SendEphemeralPost: ऐप के बॉट के रूप में या किसी उपयोगकर्ता के रूप में पोस्ट करें।
  • EnsureBotUser(&model.Bot{Username, DisplayName}): ऐप का बॉट एक बार बनाएं और उसकी ID प्राप्त करें।
  • PublishWebSocketEvent("poll_updated", payload, &model.WebsocketBroadcast{ChannelId: id}): वेब ऐप को एक इवेंट भेजें। यह custom_<app id>_poll_updated के रूप में आता है।
  • OpenInteractiveDialog: एक डायलॉग खोलें।
  • LogInfo, LogError और अन्य: सर्वर लॉग में लिखें, ऐप की ID के साथ टैग किया गया।
  • GetUser, GetChannel, HasPermissionToChannel: किसी उपयोगकर्ता के लिए कार्य करने से पहले जांचें कि कौन क्या कर सकता है।

एक ऐप क्लस्टर के हर सर्वर पर चलता है। स्टेट को केवल मेमोरी में नहीं, बल्कि की-वैल्यू स्टोर या डेटाबेस में रखें।

AI सेवा

Antimatter का सर्वर ऐप्स के लिए AI मॉडल को कॉल करता है, एडमिन द्वारा सेट किए गए मॉडल कनेक्शन के माध्यम से (एडमिन गाइड में AI देखें)। एक ऐप कभी भी प्रोवाइडर की कुंजी नहीं रखता है: यह उस व्यक्ति का नाम बताता है जिसके लिए वह कार्य कर रहा है और कहाँ, और सर्वर जाँचता है कि क्या वह व्यक्ति उस जगह कनेक्शन का उपयोग कर सकता है, उसकी सीमाओं और बजटों को लागू करता है, टोकन को उस व्यक्ति और ऐप के विरुद्ध गिनता है, और कॉल की सामग्री के बिना ऑडिट करता है।

विधियह क्या करता है
AIChat(actingUserID, channelID, teamID, *ai.ChatRequest)एक चैट कंप्लीशन: संदेश, टूल्स, संरचित आउटपुट (JSONSchema)।
AIChatStream(…)उसी का स्ट्रीमिंग संस्करण: चैनल बंद होने तक स्ट्रीम के Events पढ़ें, या कॉल रोकने के लिए Close करें।
AIEmbed(…, *ai.EmbedRequest)टेक्स्ट के एम्बेडिंग।
AITranscribe(…, *ai.TranscribeRequest)बोली से टेक्स्ट (Speech to text)।
AIDecide(…, *ai.DecideRequest)टेक्स्ट के बारे में निर्णय मॉडल (yes or no, a choice, a score) को टाइप्ड प्रश्न।
AIConnectionsForUser(userID, channelID, teamID)वहाँ एक व्यक्ति द्वारा उपयोग किए जा सकने वाले कनेक्शन, उनके मॉडल और क्षमताओं के साथ, कभी उनकी कुंजियाँ नहीं।

हर अनुरोध का ConnectionID कनेक्शन चुनता है, और Model उसके मॉडलों में से एक (कनेक्शन के डिफ़ॉल्ट के लिए खाली)। Operation उपयोग और ऑडिट रिकॉर्ड के लिए एक छोटा लेबल है, कभी सामग्री नहीं। इनकी न्यूनतम सर्वर संस्करण 11.11 है।

actingUserID को केवल उस स्रोत से लें जिसे सर्वर ने सत्यापित किया है: आपके ServeHTTP के लिए अनुरोध के Mattermost-User-Id हेडर, या हुक के संदर्भ का उपयोगकर्ता (पोस्ट के लेखक, या उस उपयोगकर्ता जिसने कमांड चलाया)। इसे कभी संदेश, वेबहुक पेयलोड, URL या किसी अन्य ऐप के अनुरोध से न लें: अन्यथा आपका ऐप किसी अन्य व्यक्ति की पहुंच खर्च कर देगा।

ये विधियाँ और ai पैकेज (server/public/ai) केवल Antimatter के मॉड्यूल के कॉपी में मौजूद हैं, Mattermost द्वारा प्रकाशित संस्करण में नहीं। इन्हें उपयोग करने के लिए, Antimatter के server/public के विरुद्ध बिल्ड करें, अपने go.mod में एक replace लाइन के साथ, उदाहरण के लिए replace github.com/mattermost/mattermost/server/public => ../antimatter/server/public। ऐसा ऐप केवल Antimatter पर चलेगा।

डेटा स्टोर करना

हर ऐप के पास सर्वर के डेटाबेस में अपना अलग की-वैल्यू स्टोर होता है:

  • KVSet(key, value), KVGet(key), KVDelete(key): वैल्यू बाइट्स होती हैं, आमतौर पर JSON। की अधिकतम 150 अक्षरों की हो सकती है।
  • KVSetWithExpiry(key, value, seconds): उस समय के बाद वैल्यू हट जाती है।
  • KVCompareAndSet(key, old, new) और KVSetWithOptions के साथ Atomic: जब कई रिक्वेस्ट या सर्वर एक ही की में लिखते हैं, तो सुरक्षित अपडेट। Polls इसी तरह हर वोट सेव करता है और टक्कर (conflict) पर दोबारा कोशिश करता है।
  • KVList(page, perPage): की, पेज-दर-पेज।

वेब ऐप हिस्सा

बंडल window.registerPlugin के साथ एक क्लास रजिस्टर करता है। इसका initialize रजिस्ट्री और Redux स्टोर प्राप्त करता है:

import manifest from './manifest';

class Plugin {
    initialize(registry, store) {
        registry.registerTranslations((locale) => translationsFor(locale));
        registry.registerPostTypeComponent('custom_poll', PollPost);
        registry.registerWebSocketEventHandler(`custom_${manifest.id}_poll_updated`, (msg) => {
            store.dispatch(pollUpdated(JSON.parse(msg.data.poll)));
        });
        registry.registerReconnectHandler(() => store.dispatch(reloadPolls()));
    }
}

window.registerPlugin(manifest.id, new Plugin());
  • साझा लाइब्रेरी। वेब ऐप React, ReactDOM, Redux, React Redux और React Intl प्रदान करता है: इन्हें webpack externals (React, ReactDOM, Redux, ReactRedux, ReactIntl) के रूप में सूचीबद्ध करें, बजाय इसके कि आप अपनी कॉपी बंडल करें। Polls के webpack.config.js को देखें।
  • अपने सर्वर हिस्से को कॉल करना। ${window.basename}/plugins/<app id>/… को Client4.getOptions() (जो mattermost-redux/client से आता है) के विकल्पों के साथ Fetch करें: वे हेडर ले जाते हैं जो सर्वर को ब्राउज़र से आने वाले रिक्वेस्ट को स्वीकार करने के लिए चाहिए।
  • आपका अपना स्टेट। registerReducer एक रिड्यूसर जोड़ता है, जो state['plugins-<app id>'] के तहत स्टोर होता है।
  • टाइप्स। Antimatter की अतिरिक्त रजिस्ट्री विधियाँ Mattermost की प्रकाशित टाइप्स में नहीं हैं: Polls webapp/src/types/host.ts में उस रजिस्ट्री की घोषणा करता है जिसका वह उपयोग करता है।

रजिस्ट्री की विधियाँ और उनके पैरामीटर कोड में दस्तावेज़ित हैं, webapp/fusion/src/plugins/registry.ts (Fusion) और webapp/channels/src/plugins/registry.ts (classic) में।

Fusion इंटरफेस का समर्थन

Antimatter के दो वेब इंटरफेस हैं: Fusion, जो डिफ़ॉल्ट है, और क्लासिक। दोनों एक ही ऐप बंडल लोड करते हैं और एक ही रजिस्ट्री रखते हैं, लेकिन Fusion कई चीज़ें स्वयं बनाता है और प्लगइन कंपोनेंट्स को अपनी तरह से रखता है। अपने ऐप को दोनों में टेस्ट करें।

इंटरफेस का पता लगाना

किसी भी ऐप के लोड होने से पहले, दोनों इंटरफेस window.antimatterWebUI को 'fusion' या 'classic' सेट करते हैं, और पेज के रूट एलिमेंट पर data-am-web-ui के रूप में वही मान। Mattermost पर, न तो यह सेट होता है।

export function isFusionUI(): boolean {
    return window.antimatterWebUI === 'fusion' || document.documentElement.dataset.amWebUi === 'fusion';
}

बंडल्ड ऐप्स इसका उपयोग अलग-अलग ढंग से ड्रॉ करने के लिए करते हैं। Polls, Fusion के स्टाइल में अपने पोल कार्ड और निर्माण डायलॉग ड्रॉ करता है, और Fusion में /poll के लिए अपना खुद का डायलॉग खोलता है। Calls और Voice channels, Fusion द्वारा अपने कॉल इंटरफ़ेस से बदले जाने वाले साइडबार और टोस्ट कंपोनेंट्स को छोड़ देते हैं। Agents ऐप, होवर टूलबार के बजाय, संदेश मेन्यू में अपने AI कार्रवाओं को अधिक कार्रवाएं के तहत एक समूह के रूप में रखता है। Fusion के अपने CSS क्लासेस (जिनमें am- प्रिफिक्स है) और उसका डायलॉग लेयर (am-layer एलिमेंट) ही वे चीज़ें हैं जिनका उपयोग ये ऐप्स इंटरफ़ेस से मेल खाने के लिए करते हैं; ये एक दस्तावेज़ीकृत, स्थिर API नहीं हैं।

Fusion में प्लगइन कंपोनेंट्स कहाँ दिखाई देते हैं

रेजिस्ट्री मेथडFusion में यह कहाँ दिखाई देता है
registerAppBarComponent, registerChannelHeaderButtonActionऐप रेल, दाएँ किनारे पर आइकनों का स्तंभ, जो आपके अवतार और Antimatter के बारे में बटन के बीच में हय। चैनल हेडर के बटन भी वहाँ जाते हैं, न कि चैनल हेडर में। जब सिस्टम एडमिन ऐप्स बार निष्क्रिय करें (ExperimentalSettings.DisableAppBar, डिफ़ॉल्ट में बंद) चालू कर देता हय, तो यह छिप जाता हय।
registerRightHandSidebarComponentदाईं ओर का पैनल। लोग इसे उसके किनारे को खींचकर आकार बदल सकते हैं, और इसे एक नए विंडो में खोल सकते हैं। यदि ऐप एक प्रोडक्ट भी है, तो पैनल पूरे पेज में खोलें का विकल्प देता है। नए विंडो में पैनल देखें।
registerChannelHeaderIconचैनल हेडर में, कॉल बटनों के बाद आने वाले आइकन; फ़ोन पर, हेडर के अधिक मेनू में।
registerCallButtonActionचैनल हेडर का कॉल बटन; कई प्रोवाइडर एक कॉल के अन्य तरीके मेनू साझा करते हैं।
registerPostDropdownMenuActionसंदेश का संदेश कार्रवाई (⋯) मेन्यू, इसके अधिक कार्रवाएं सबमेन्यू में; साथ ही थ्रेड पैनल के थ्रेड कार्रवाइयां मेन्यू में, अधिक कार्रवाएं के तहत। Fusion हर कार्रवा के पहले ऐप का आइकन, भूरे रंग में, ड्रॉ करता है: बंडल्ड ऐप्स के लिए उसका अपना आइकन, अन्यथा ऐप रेल में दिखाया गया आइकन, अन्यथा एक तटस्थ आइकन। कार्रवा के टेक्स्ट के आरंभ में टेक्स्ट के बिना कोई एलिमेंट (जो कि ऐप स्वयं ड्रॉ करता है) छिपा दिया जाता है।
registerPostDropdownSubMenuActionएक समूह, जिसमें एक हेडिंग है, उसी अधिक कार्रवाएं सबमेन्यू में, एकल कार्रवाओं के बाद। संदेश मेन्यू में किसी भी ऐप को अपना अलग प्रविष्टि नहीं मिलती।
registerPostActionComponentमैसेज होवर टूलबार, ⋯ बटन से पहले।
registerPostTypeComponent, registerPostMessageAttachmentComponent, registerPostHeaderComponent, registerPostWillRenderEmbedComponentमैसेज स्वयं: इसका बॉडी, इसके टेक्स्ट के नीचे, समय के बाद, और लिंक प्रीव्यू की जगह।
registerChannelHeaderMenuActionचैनल का चैनल विकल्प मेनू (साइडबार में चैनल पर ⋯, या राइट-क्लिक), जो प्लग आइकन के साथ आइटम के रूप में दिखाई देते हैं।
registerMainMenuActionकम्युनिटी मेनू (चैनल सूची के शीर्ष पर कम्युनिटी का नाम), अधिक कार्रवाएं के नीचे, ऐप के अनुसार समूहित।
registerSidebarBrowseOrAddChannelMenuActionकम्युनिटी मेनू, चैनल बनाएं और श्रेणी बनाएं के बाद।
registerFileUploadMethodकंपोज़र का + मेनू (संदेश में जोड़ें), फ़ाइल अपलोड करें के बाद।
registerPostEditorActionComponentकंपोज़र का फॉर्मेटिंग बार, जब फॉर्मेटिंग दिखाई देती है।
registerAIActionMenuItemComponentकंपोज़र का AI मेन्यू।
registerFileDropdownMenuAction, registerFilePreviewComponentफ़ाइल का ऑप्शन मेन्यू, और फ़ाइल प्रीव्यू।
registerUserSettingsप्रोफ़ाइल और सेटिंग्स, प्लगइन्स हेडिंग के नीचे हर ऐप के लिए एक टैब।
registerChannelSettingsTabचैनल सेटिंग्स डायलॉग, ऐप्स हेडिंग के नीचे।
registerPopoverUserAttributesComponent, registerPopoverUserActionsComponentप्रोफ़ाइल कार्ड जो तब दिखाई देता है जब आप किसी नाम या अवतार पर क्लिक करते हैं।
registerLeftSidebarHeaderComponent, registerSidebarChannelLinkLabelComponent, registerChannelIconOverride, registerBottomTeamSidebarComponentकिसी समुदाय के चैनल सूची का शीर्ष, किसी चैनल के नाम के पास, चैनल का आइकन, और बाईं ओर समुदाय रेल का निचला हिस्सा।
registerChannelIntro, registerChannelIntroButtonAction, registerChannelToastComponent, registerNewMessagesSeparatorActionComponent, registerChannelComposerBannerComponent, registerComposerPlaceholderकिसी चैनल के इतिहास की शुरुआत, संदेश सूची का शीर्ष, अपठित पंक्ति, कंपोज़र के ऊपर, और कंपोज़र का प्लेसहोल्डर।
registerChannelTypeOptionचैनल बनाएं डायलॉग के चैनल प्रकार।
registerSearchComponentsचैनल हेडर की खोज बॉक्स: जब यह खाली होती है, तो ऐप स्कोप दिखाई देते हैं।
registerUserGuideDropdownMenuActionAntimatter के बारे में (ऐप रेल का निचला हिस्सा), सहायता के नीचे।
registerProductप्रोडक्ट के URL पर पूर्ण पृष्ठ, जो Antimatter के बारे में में उत्पाद के तहत सूचीबद्ध हैं। प्रोडक्ट पृष्ठ क्लासिक पृष्ठ फ्रेम को बनाए रखते हैं, जिसका प्रोडक्ट मेन्यू तभी दिखाई देता है जब कोई ऐप एक प्रोडक्ट पंजीकृत करता है या उसमें एक प्रविष्टि जोड़ता है। बंडल्ड Kanban और Playbooks ऐप्स अब प्रोडक्ट पंजीकृत नहीं करते हैं: उनके पृष्ठ उनके साइड पैनल में खुलते हैं।
registerRootComponent, registerGlobalComponent, registerMessageWillBePostedHook, registerSlashCommandWillBePostedHook, registerMessageWillFormatHook, registerFilesWillUploadHook, registerDesktopNotificationHook, registerWebSocketEventHandler, registerReducer, registerTranslations जैसे हुक्सक्लासिक इंटरफ़ेस की तरह कार्य करें।
registerAdminConsolePlugin, registerAdminConsoleCustomSettingसिस्टम कंसोल, जो दोनों इंटरफ़ेस में क्लासिक है।

जो Fusion नहीं दिखाता या नहीं चलाता

  • registerPostDropdownMenuComponent (संदेश मेनू में एक कंपोनेंट), registerPostCardTypeComponent (दाईं ओर पैनल में पोस्ट का कार्ड) और registerProductSwitcherMenuItem Fusion के मुख्य दृश्यों में नहीं दिखाई देते। पहले के बजाय registerPostDropdownMenuAction का उपयोग करें।
  • जब कोई संदेश Fusion में संपादित किया जाता है, तो registerMessageWillBeUpdatedHook नहीं चलता। इसके बजाय, संपादन की जांच सर्वर पर MessageWillBeUpdated हुक के साथ करें।
  • registerActionAfterChannelCreation केवल क्लासिक चैनल निर्माण डायलॉग से चलता है, जिसे Fusion अपने चैनल बनाएं डायलॉग में अधिक विकल्प… से खोलता है।
  • ऐप बार कंपोनेंट उन सभी supportedProductIds के साथ दिखाई देते हैं।
  • registerNeedsTeamRoute और registerCustomRoute से आने वाले रास्ते उत्पादों की तरह क्लासिक पृष्ठ फ्रेम बनाए रखते हैं।

केवल Antimatter के पास होने वाले हुक्स

ये रजिस्ट्री विधियां Mattermost पर मौजूद नहीं हैं। कॉल करने से पहले जांचें कि विधि मौजूद है, ताकि ऐप वहाँ भी काम करे:

if (typeof registry.registerComposerMenuItem === 'function') {
    registry.registerComposerMenuItem({
        text: 'Create a poll',
        icon: <i className='icon icon-chart-bar'/>,
        fusionIcon: 'poll',
        action: ({channelId, rootId}) => openPollCreation(channelId, rootId),
    });
} else {
    registry.registerChannelHeaderButtonAction(icon, (channel) => openPollCreation(channel.id), 'Create a poll', 'Create a poll');
}
विधियह क्या जोड़ता है
registerComposerMenuItem({text, icon, fusionIcon, action, shouldRender})संदेश बॉक्स के + मेनू में एक आइटम, दोनों इंटरफ़ेस में। action को {channelId, rootId} प्राप्त होता है।
registerEmojiPickerTab({id, label, icon, fusionIcon, order, component, shouldRender, replacesGifPicker})संदेश बॉक्स के इमोजी पिकर का एक टैब, जैसे कि GIFs या स्टिकर। Fusion में, 10 से कम order वाले टैब, Emoji टैब से पहले आते हैं। कंपोनेंट channelId, rootId, filter, onFilterChange, onSelectDone, insertText, isFusion और, Fusion में, pickerKit (नीचे देखें) प्राप्त करता है।
registerChannelViewPanel({matcher, component})चैनल हेडर और संदेशों के बीच एक पैनल, उन चैनलों के लिए जो matcher(state, channel) चुनता है। यह संदेशों और कंपोज़र को छिपा सकता है (setMessagesVisible), जैसे वॉइस चैनल करते हैं।
registerSidebarChannelFooterComponent(component)साइडबार में चैनल के नीचे एक पंक्ति, जैसे उसके कॉल में मौजूद लोग। यह channel प्राप्त करता है।
registerIntegrationsOption({title, description, fusionIcon, action, shouldRender})केवल Fusion: इंटीग्रेशन पेज पर एक एंट्री, कस्टम इमोजी के पास। GIFs ऐप वहाँ अपने कस्टम GIFs और स्टिकर जोड़ता हय।

इमोजी पिकर में व्यू जोड़ें और प्रबंधित करें

Fusion में, एक इमोजी पिकर टैब को pickerKit प्रॉप मिलता है: वे कंपोनेंट जो इमोजी टैब अपने add और manage व्यूज़ के लिए उपयोग करता है, ताकि टैब के व्यूज़ एक जैसे दिखें और पिकर के अंदर ही रहें। GIFs और Memes ऐप्स इसका उपयोग GIFs, स्टिकर और मीम टेम्पलेट्स जोड़ने और प्रबंधित करने के लिए करते हैं। इसमें version (1), हुक्स usePickerView और useOnEscape, और कंपोनेंट PickerViewButtons (सर्च बॉक्स के बगल में + और manage बटन), PickerViewFrame (अपनी बैक एरो के साथ एक व्यू), PickerNotice, PickerField, PickerSearch, PickerDropZone और PickerManageList शामिल हैं। Escape दबाते ही व्यू से टैब के ग्रिड पर वापस चला जाता है। जांचें कि यह प्रॉप मौजूद है: अन्य होस्ट इसे छोड़ देते हैं, और तब टैब अपने डायलॉग्स को स्वयं संभालता है। टाइप्स webapp/fusion/src/types/store/plugins.ts (EmojiPickerTabProps) में और कंपोनेंट webapp/fusion/src/fusion/popovers/picker_views.tsx में हैं।

नई विंडो में पैनल

Fusion, registerRightHandSidebarComponent के कंपोनेंट को दो और प्रॉप देता है, ताकि नई विंडो में खोलें पैनल को ऐप के होम पर नहीं, बल्कि वहाँ खोल दे जहाँ वह पहले था:

  • setPanelState(state): जब पैनल की जगह बदले, इसे कॉल करें, आपके ऐप द्वारा परिभाषित एक छोटे स्ट्रिंग के साथ, जैसे 'note/<id>' या 'board/<id>/card/<id>', या होम पर null। यह विंडो के एड्रेस में जाता है, इसलिए इसमें कभी भी कोई गुप्त जानकारी न डालें। 512 कैरेक्टर से लंबा स्टेट, या जिसमें कंट्रोल कैरेक्टर हों, हटा दिया जाता है।
  • initialPanelState: नई विंडो में, वह स्टेट जो पैनल के पास थी, या undefined। इसे अविश्वास योग्य इनपुट के रूप में मानें: साइन-इन किए गए व्यक्ति के रूप में आइटम को फिर से फेच करें, और यदि वे उसे खोल नहीं पाते हैं, तो ऐप का होम या नॉट-फाउंड व्यू दिखाएं।

जब पैनल बंद होता है या किसी अन्य ऐप का पैनल उसकी जगह लेता है, तो स्टेट भूल जाया जाता है। यदि आप स्वयं किसी पैनल की विंडो खोलना चाहते हैं, तो popoutRhsPlugin (जो window.WebappUtils.popouts में भी है) स्टेट को एक वैकल्पिक पाँचवें आर्ग्यूमेंट के रूप में लेता है। Notes, Whiteboard, Todo, Kanban और Playbooks इसका उपयोग करते हैं।

fusionIcon Fusion के आइकन सेट का एक आइकन नामित करता है (उदाहरण के लिए poll, sticker, calendar या image; देखें webapp/fusion/src/fusion/components/icon)। Fusion इसे icon की जगह दिखाता है, ताकि आइटम अन्य आइटम से मेल खाए।

सेटिंग्स

plugin.json में settings_schema सिस्टम कंसोल › प्लगइन्स में ऐप के पेज का वर्णन करता हय:

"settings_schema": {
  "header": "Forecasts come from the weather service below.",
  "settings": [
    {"key": "ServiceURL", "display_name": "Service URL", "type": "text", "default": "https://api.example.com"},
    {"key": "Units", "display_name": "Units", "type": "dropdown", "default": "metric",
     "options": [{"display_name": "Celsius", "value": "metric"}, {"display_name": "Fahrenheit", "value": "imperial"}]},
    {"key": "EnableAlerts", "display_name": "Severe weather alerts", "type": "bool", "default": false,
     "help_text": "Post alerts in the channels that follow a city."}
  ]
}

सेटिंग्स के प्रकार: bool, text, longtext, number, dropdown, radio, generated (एक रैंडम सीक्रेट), username, और custom (जो वेब ऐप भाग द्वारा registerAdminConsoleCustomSetting के साथ पंजीकृत किए गए कंपोनेंट द्वारा बनाया जाता है)। सेटिंग्स को sections में समूहीकृत किया जा सकता है।

सर्वर भाग उन्हें एक स्ट्रक्ट में पढ़ता है जिसके फ़ील्ड कुंजियों से मेल खाते हैं:

type configuration struct {
	ServiceURL   string
	Units        string
	EnableAlerts bool
}

func (p *Plugin) OnConfigurationChange() error {
	var cfg configuration
	if err := p.API.LoadPluginConfiguration(&cfg); err != nil {
		return err
	}
	p.setConfiguration(&cfg)
	return nil
}

एडमिन इन्हें कॉन्फ़िगरेशन में भी सेट कर सकते हैं, PluginSettings.Plugins.<app id> के तहत, लोअर केस में कुंजियों के साथ (देखें एडमिन गाइड)।

अनुवाद

Antimatter 37 भाषाओं में अनुवादित है, और बंडल्ड ऐप भी। वेब ऐप भाग इसे इस प्रकार करता है:

  1. हर स्ट्रिंग को React Intl के साथ लिखें, एक ID और उसके अंग्रेज़ी टेक्स्ट के साथ: <FormattedMessage id='polls.create' defaultMessage='Create a poll'/> या defineMessages। ID के आगे आपके ऐप का नाम प्रिफ़िक्स करें।
  2. अंग्रेज़ी स्ट्रिंग्स को webapp/i18n/en.json में निकालें (बंडल्ड ऐप formatjs extract का उपयोग करते हैं)।
  3. उसके पास हर भाषा के लिए एक फ़ाइल जोड़ें, समान ID के साथ: fr.json, de.json, pt-BR.json, zh-CN.json…
  4. इन्हें वेब ऐप को दें, जो उपयोगकर्ता की भाषा पूछता है:
    import fr from '../i18n/fr.json';
    import ptBR from '../i18n/pt-BR.json';
    
    const catalogs = {fr, 'pt-BR': ptBR};
    
    export function translationsFor(locale) {
        return catalogs[locale] || catalogs[locale.split('-')[0]] || {};
    }
    
    registry.registerTranslations(translationsFor);

अगर कोई स्ट्रिंग नहीं मिलती, तो वह अपने अंग्रेज़ी defaultMessage पर वापस आ जाती है। उन स्ट्रिंग्स के लिए जो सर्वर भाग भेजता है (कमांड के जवाब, बॉट संदेश), pluginapi/i18n पैकेज बंडल में शामिल अनुवाद फ़ाइलें (उदाहरण के लिए assets/i18n) लोड करता है और हर उपयोगकर्ता की भाषा में एक लोकलाइज़र देता है।

बिल्ड, इंस्टॉल और डीबग करें

आपको Go और Node.js (ऐप के .nvmrc में दी गई संस्करण) की ज़रूरत है। ऐप की फ़ोल्डर में:

Commandयह क्या करता है
make distहर प्लेटफ़ॉर्म के लिए सर्वर भाग और वेब ऐप भाग का बिल्ड करता है, और बंडल को dist/<app id>-<version>.tar.gz में लिखता है।
make testGo और JavaScript टेस्ट चलाता है।
make check-styleकोड का लींट करता है (golangci-lint, ESLint) और TypeScript टाइप्स की जाँच करता है।
make deployबिल्ड करता है, फिर सर्वर पर बंडल को इंस्टॉल और सक्षम करता है।
make watch, make deploy-from-watchपहला हर बार आप सहेजते समय वेब ऐप भाग का पुनः बिल्ड करता है; दूसरा जो उसने बनाया है उसे बंडल करता है और सर्वर पर इंस्टॉल करता है।
make enable, make disable, make resetसर्वर पर ऐप को सक्षम, अक्षम या पुनः आरंभ करें।
make logs, make logs-watchसर्वर लॉग में ऐप की पंक्तियाँ दिखाएँ।
make attachचल रहे सर्वर भाग से Delve डीबगर को जोड़ता है (पहले AM_DEBUG=1 के साथ बिल्ड करें)।

make deploy और अन्य सर्वर कमांड्स, अगर वे एक लोकल मोड सोकेट पाते हैं (/var/tmp/antimatter_local.socket, या AM_LOCALSOCKETPATH), तो उसका उपयोग करते हैं: इन्हें सर्वर की मशीन पर लोकल मोड चालू रखकर चलाएँ। अन्यथा वे MM_SERVICESETTINGS_SITEURL द्वारा दिए गए सर्वर पर AM_ADMIN_TOKEN, या AM_ADMIN_USERNAME और AM_ADMIN_PASSWORD के साथ साइन इन करते हैं। बिल्ड टूल्स अभी तक साइट URL के लिए AM_ नाम नहीं पढ़ते।

बंडल को हाथ से इंस्टॉल करें

  • एक सिस्टम एडमिन AM_PLUGINSETTINGS_ENABLEUPLOADS=true से अपलोड्स की अनुमति देता है (डिफ़ॉल्ट रूप से बंद; कोई System Console स्विच नहीं है)।
  • फिर सिस्टम कंसोल › प्लगइन्स › प्लगइन प्रबंधन › प्लगइन अपलोड करें, या amctl plugin add weather.tar.gz के बाद amctl plugin enable com.example.weather।
  • यदि सर्वर पर प्लगइन सिग्नेचर आवश्यक करें चालू हय, तो वह केवल उन बंडल्स को स्वीकार करता हय जो उसकी विश्वसनीय कुंजी से साइन किये गए हों; देखें अन्य ऐप इंस्टॉल करना।
डेवलपमेंट सर्वर पर तेज़ पुनरावृत्तियों के लिए, सर्वर पर AM_SERVICESETTINGS_ENABLEDEVELOPER=true सेट करें: तब त्रुटियाँ अपनी विस्तृत जानकारी के साथ आती हैं। बंडल्ड ऐप्स के Makefiles, जब MM_SERVICESETTINGS_ENABLEDEVELOPER आपके शेल में सेट होता है, तब केवल आपके प्लेटफ़ॉर्म के लिए सर्वर भाग का बिल्ड करते हैं।