Skip to content

Commit

Permalink
Merge pull request #14124 from nextcloud/feat/14017/sample-conversations
Browse files Browse the repository at this point in the history
feat(conversations): Add new sample conversation "💡 Let's get started!"
  • Loading branch information
nickvergessen authored Jan 15, 2025
2 parents a4db9bf + 2825400 commit fe2dd66
Show file tree
Hide file tree
Showing 45 changed files with 615 additions and 55 deletions.
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,4 @@
* `schedule-meeting` (local) - Whether logged-in participants can schedule meetings
* `config => chat => has-translation-task-providers` (local) - When true, translations can be done using the [OCS TaskProcessing API](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-taskprocessing-api.html).
* `config => conversations => list-style` (local) - Whether conversation list should appear in certain way
* `config => conversations => description-length` (local) - The maximum length for conversation descriptions, currently 2000. Before this config was added the implicit limit was 500, since the existance of the feature capability `room-description`.
1 change: 1 addition & 0 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
| `share:password` | No | Video verification to verify the identity of the share recipient | Share token |
| `room` | Yes | Room is a breakout room | Token of the main/parent conversation |
| `phone` | Yes | Room is created when calling a phone number with SIP dial-out | `phone` (not set atm, just used for the default avatar) |
| `sample` | No | Room is a sample conversation | User ID the sample |

### Read-only states
* `0` Read-write
Expand Down
6 changes: 3 additions & 3 deletions docs/conversation.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ Get all (for moderators and in case of "free selection") or the assigned breakou
* Endpoint: `/room/{token}/description`
* Data:

| field | type | Description |
|---------------|--------|--------------------------------------|
| `description` | string | New description for the conversation |
| field | type | Description |
|---------------|--------|--------------------------------------------------------------------------------------------|
| `description` | string | New description for the conversation (limited to 2.000 characters, was 500 before Talk 21) |

* Response:
- Status code:
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* [Configuring eturnal](eturnal.md)
* [Scalability](scalability.md)
* [Call experience](call-experience.md)
* [Sample conversations](sample-conversations.md)
* [Occ commands](occ.md)
* [Bots](bot-list.md)
* [Matterbridge integration](matterbridge.md)
Expand Down
71 changes: 71 additions & 0 deletions docs/sample-conversations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Sample conversations

In Hub 10 (Nextcloud 31) we introduced new sample conversations to better showcase various features of Nextcloud Talk. The default sample conversations are translated just like Nextcloud itself is, so it should be shown to the user in a language they understand.

## Custom samples

It is also possible to replace the sample conversations. For this you need to configure the `samples_directory` app config of the spreed app to point to a directory.
```shell
sudo -u www-data php /var/www/nextcloud/occ \
config:app:set \
spreed samples_directory \
--value '/this/is/the/configured/path'
```

In the directory you put a subdirectory of each language that you want to support. A directory structure looks like the following:

```
/this/is/the/configured/path
├─ de/
├─ en/
├─ es/
└─ fr/
```
The chosen language is picked from the user language, falling back to the `default_language` and can be overwritten with the `force_language` system config. It's also falling back from languages like `de_DE` to `de` if the first one is picked but does not exist.

In each of the languages you can have a unique set of sample Markdown files.

### Conversation information

The first section of the Markdown files must contain the following information, prefixed with the matching keyword:

```
NAME: Let´s get started!
EMOJI: 💡
COLOR: #0082c9
---
```

End this segment with 3 dashes `---`.

Optionally you can have a description in the second section, indicated by the `DESCRIPTION:` marker as a beginning and ending again with `---`:

```markdown
DESCRIPTION:

This is a *sample* description! Including **Markdown** support.

---
```

Please note that descriptions are currently limited to 2.000 characters.

### Messages

Afterwards you can have unlimited amount of messages, each separated by `---`. Messages support markdown and mentions.

Additionally, the following additional features can be used:

- Reply: `{REPLY}` (at the start of the message) The message will be posted as a reply to the previous message
- Reactions: `{REACTION:👍}` (anywhere in the message) will be removed and a reaction with the given emoji will be down by the system
- File link: `{FILE:Readme.md}` (anywhere in the message) will be removed from the message and a link to the specified file inside the user's Files app root will be posted

## Disable samples

If you don't like the samples we provide, and you don't want to write your own samples, you can also disable them by setting the `create_samples` app config of the spreed app to false:
```shell
sudo -u www-data php /var/www/nextcloud/occ \
config:app:set \
spreed create_samples \
--value false --type boolean
```
72 changes: 72 additions & 0 deletions docs/samples/en/lets-get-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
NAME: Let´s get started!
EMOJI: 💡
COLOR: #0082c9
---
{DESCRIPTION}

**Nextcloud Talk** is a secure, self-hosted communication platform that integrates seamlessly with the Nextcloud ecosystem.

#### Key Features of Nextcloud Talk:

* Chat and messaging in private and group chats
* Voice and video calls
* File sharing and integration with other Nextcloud apps
* Customizable conversation settings, moderation and privacy controls
* Web, desktop and mobile (iOS and Android)
* Private & secure communication

Find out more in the [user documentation](https://docs.nextcloud.com/server/latest/user_manual/en/talk/index.html).

---

# Welcome to Nextcloud Talk

Nextcloud Talk is a private and powerful messaging app that integrates with Nextcloud. Chat in private or group conversations, collaborate over voice and video calls, organize webinars and events, customize your conversations and more.

---

## 🎨 Format texts to create rich messages

In Nextcloud Talk, you can use Markdown syntax to format your messages. For example, apply **bold** or *italic* formatting, or `highlight texts as code`. You can even create tables and add headings to your text.

Need to fix a typo or change formatting? Edit your message by clicking "Edit message" in the message menu.

---

## 🔗 Add attachments and links

Attach files from your Nextcloud Hub using the "+" button. Share items from Files and various Nextcloud apps. Some apps even support interactive widgets, for example, the Text app.

{FILE:Readme.md}

---

## 💭 Let the conversations flow: mention users, react to messages and more

You can mention everybody in the conversation @all or mention specific participants by typing "@" and picking their name from the list.

{REACTION:😍}
{REACTION:👍}

---

{REPLY}
You can reply to messages, forward them to other chats and people, or copy message content.

---

## ✨ Do more with Smart Picker

Simply type "/" or go to the "+" menu to open the Smart Picker where you can attach various content to your messages. You can configure the Smart Picker to be able to add items from Nextcloud apps, GIFs, map locations, AI generated content and much more.

---

## ⚙️ Manage conversation settings

In the conversation menu, you can access various settings to manage your conversations, such as:
* Edit conversation info
* Manage notifications
* Apply numerous moderation rules
* Configure access and security
* Enable bots
* and more!
2 changes: 2 additions & 0 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Legend:
| `matterbridge_binary` | string | | No | | Path to the matterbridge binary file |
| `bridge_bot_password` | string | | No | | Automatically generated password of the matterbridge bot user profile |
| `default_attachment_folder` | string | `/Talk` | No | | Specify default attachment folder location |
| `samples_directory` | string | | No | | Specify a readable directory that contains other sample conversation data |
| `start_calls` | int | `0` | Yes | 🖌️ | Who can start a call, see [constants list](constants.md#start-call) |
| `max_call_duration` | int | `0` | No | | Maximum duration of a call in seconds, 0 for unlimited. Federated calls will be terminated based on the setting of the host server. Calls are ended via a background job, so system cron should be used and calls will last a bit longer (until the next execution of the cron). |
| `max-gif-size` | int | `3145728` | No | | Maximum file size for clients to render gifs previews with animation |
Expand Down Expand Up @@ -116,5 +117,6 @@ Legend:
| `delete_one_to_one_conversations` | string<br>`1` or `0` | `0` | No || Whether one-to-one conversations can be left by either participant or should be deleted when one participant leaves |
| `enable_matterbridge` | string<br>`1` or `0` | `0` | No | 🖌️ | Whether the Matterbridge integration is enabled and can be configured |
| `force_passwords` | string<br>`1` or `0` | `0` | No || Whether public chats are forced to use a password |
| `create_samples` | string<br>`1` or `0` | `1` | No || Create sample conversations (the content can be overwritten by providing files in a provided `samples_directory` app config) |
| `inactivity_lock_after_days` | int | `0` | No | | A duration (in days) after which rooms are locked. Calculated from the last activity in the room. |
| `inactivity_enable_lobby` | string<br>`1` or `0` | `0` | No | | Additionally enable the lobby for inactive rooms so they can only be read by moderators. |
2 changes: 2 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
use OCA\Talk\Listener\GroupMembershipListener;
use OCA\Talk\Listener\NoteToSelfListener;
use OCA\Talk\Listener\RestrictStartingCalls as RestrictStartingCallsListener;
use OCA\Talk\Listener\SampleConversationsListener;
use OCA\Talk\Listener\UserDeletedListener;
use OCA\Talk\Maps\MapsPluginLoader;
use OCA\Talk\Middleware\CanUseTalkMiddleware;
Expand Down Expand Up @@ -199,6 +200,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(BeforeRoomsFetchEvent::class, ChangelogListener::class);
$context->registerEventListener(RoomDeletedEvent::class, ChatListener::class);
$context->registerEventListener(BeforeRoomsFetchEvent::class, NoteToSelfListener::class);
$context->registerEventListener(BeforeRoomsFetchEvent::class, SampleConversationsListener::class);
$context->registerEventListener(AttendeesAddedEvent::class, SystemMessageListener::class);
$context->registerEventListener(AttendeeRemovedEvent::class, SystemMessageListener::class);
$context->registerEventListener(AttendeesRemovedEvent::class, SystemMessageListener::class);
Expand Down
2 changes: 2 additions & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class Capabilities implements IPublicCapability {
'conversations' => [
'can-create',
'list-style',
'description-length',
],
'federation' => [
'enabled',
Expand Down Expand Up @@ -236,6 +237,7 @@ public function getCapabilities(): array {
'can-create' => $user instanceof IUser && !$this->talkConfig->isNotAllowedToCreateConversations($user),
'force-passwords' => $this->talkConfig->isPasswordEnforced(),
'list-style' => $this->talkConfig->getConversationsListStyle($user?->getUID()),
'description-length' => Room::DESCRIPTION_MAXIMUM_LENGTH,
],
'federation' => [
'enabled' => false,
Expand Down
39 changes: 39 additions & 0 deletions lib/Chat/ChatManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,45 @@ public function addChangelogMessage(Room $chat, string $message): IComment {
return $comment;
}

/**
* Post a new message to the given chat.
*
* @param Room $chat
* @param string $message
* @return IComment
*/
public function postSampleMessage(Room $chat, string $message, string $replyTo): IComment {
$comment = $this->commentsManager->create(Attendee::ACTOR_GUESTS, Attendee::ACTOR_ID_SAMPLE, 'chat', (string)$chat->getId());

if ($replyTo) {
$comment->setParentId($replyTo);
}
$comment->setMessage($message, self::MAX_CHAT_LENGTH);
$comment->setCreationDateTime($this->timeFactory->getDateTime());
$comment->setVerb(self::VERB_MESSAGE); // Has to be 'comment', so it counts as unread message
$metaData = [
Message::METADATA_CAN_MENTION_ALL => true,
];
$comment->setMetaData($metaData);

$event = new BeforeSystemMessageSentEvent($chat, $comment);
$this->dispatcher->dispatchTyped($event);
try {
$this->commentsManager->save($comment);

// Update last_message
$this->roomService->setLastMessage($chat, $comment);
$this->unreadCountCache->clear($chat->getId() . '-');

$event = new SystemMessageSentEvent($chat, $comment);
$this->dispatcher->dispatchTyped($event);
} catch (NotFoundException $e) {
}
$this->cache->remove($chat->getToken());

return $comment;
}

/**
* Sends a new message to the given chat.
*
Expand Down
18 changes: 13 additions & 5 deletions lib/Chat/Parser/Changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Events\MessageParseEvent;
use OCA\Talk\Model\Attendee;
use OCP\Defaults;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Server;

/**
* @template-implements IEventListener<Event>
Expand All @@ -28,13 +30,19 @@ public function handle(Event $event): void {
return;
}

if ($chatMessage->getActorType() !== Attendee::ACTOR_GUESTS ||
$chatMessage->getActorId() !== Attendee::ACTOR_ID_CHANGELOG) {
if ($chatMessage->getActorType() !== Attendee::ACTOR_GUESTS) {
return;
}

$l = $chatMessage->getL10n();
$chatMessage->setActor(Attendee::ACTOR_BOTS, Attendee::ACTOR_ID_CHANGELOG, $l->t('Talk updates ✅'));
$event->stopPropagation();
if ($chatMessage->getActorId() === Attendee::ACTOR_ID_CHANGELOG) {
$l = $chatMessage->getL10n();
$chatMessage->setActor(Attendee::ACTOR_BOTS, Attendee::ACTOR_ID_CHANGELOG, $l->t('Talk updates ✅'));
$event->stopPropagation();
}

if ($chatMessage->getActorId() === Attendee::ACTOR_ID_SAMPLE) {
$theme = Server::get(Defaults::class);
$chatMessage->setActor(Attendee::ACTOR_BOTS, Attendee::ACTOR_ID_SAMPLE, $theme->getName());
}
}
}
25 changes: 22 additions & 3 deletions lib/Chat/SystemMessage/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OCA\Talk\Room;
use OCA\Talk\Service\NoteToSelfService;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\SampleConversationsService;
use OCA\Talk\TalkSession;
use OCA\Talk\Webinary;
use OCP\AppFramework\Utility\ITimeFactory;
Expand Down Expand Up @@ -155,7 +156,7 @@ protected function sendSystemMessageAboutCallLeft(ParticipantModifiedEvent $even
}

protected function sendSystemMessageAboutConversationCreated(RoomCreatedEvent $event): void {
if ($event->getRoom()->getType() === Room::TYPE_CHANGELOG || $this->isCreatingNoteToSelfAutomatically($event)) {
if ($event->getRoom()->getType() === Room::TYPE_CHANGELOG || $this->isCreatingNoteToSelfAutomatically($event) || $this->isCreatingSample($event)) {
$this->sendSystemMessage($event->getRoom(), 'conversation_created', forceSystemAsActor: true);
} else {
$this->sendSystemMessage($event->getRoom(), 'conversation_created');
Expand All @@ -176,7 +177,7 @@ protected function sendSystemMessageAboutConversationRenamed(RoomModifiedEvent $

protected function sendSystemMessageAboutRoomDescriptionChanges(RoomModifiedEvent $event): void {
if ($event->getNewValue() !== '') {
if ($this->isCreatingNoteToSelf($event)) {
if ($this->isCreatingNoteToSelf($event) || $this->isCreatingSample($event)) {
return;
}

Expand Down Expand Up @@ -562,7 +563,7 @@ protected function getCallRecordingPrefix(RoomModifiedEvent $event): string {

protected function avatarChanged(RoomModifiedEvent $event): void {
if ($event->getNewValue()) {
if ($this->isCreatingNoteToSelf($event)) {
if ($this->isCreatingNoteToSelf($event) || $this->isCreatingSample($event)) {
return;
}

Expand Down Expand Up @@ -596,6 +597,24 @@ protected function isCreatingNoteToSelf(RoomModifiedEvent $event): bool {
return false;
}

protected function isCreatingSample(ARoomEvent $event): bool {
if ($event->getRoom()->getType() !== Room::TYPE_GROUP) {
return false;
}

$exception = new \Exception();
$trace = $exception->getTrace();

foreach ($trace as $step) {
if (isset($step['class']) && $step['class'] === SampleConversationsService::class &&
isset($step['function']) && $step['function'] === 'initialCreateSamples') {
return true;
}
}

return false;
}

protected function isCreatingNoteToSelfAutomatically(RoomCreatedEvent $event): bool {
if ($event->getRoom()->getType() !== Room::TYPE_NOTE_TO_SELF) {
return false;
Expand Down
Loading

0 comments on commit fe2dd66

Please sign in to comment.