P. キアネセンスとP. アレニーには、幻覚を引き起こす化合物であるサイロシビン(シロシビン) が含まれている。サイロシビンを含むシビレタケ属のキノコは数百種にのぼり、最近では、こうしたキノコには心的外傷後ストレス障害(PTSD)やうつ病の治療に有効な可能性があるとして注目が集まっている。米国では合法化の支持が広がり、精神疾患に対する治療効果を示す証拠も積み上がりつつある(編注:日本では麻薬原料植物として規制)。
⇧ 流石は、ステロイド大国合衆国、大麻に続き、マジックマッシュルームまで合法化を進めてるとは。
Wikipediaの情報によると、
マジックマッシュルーム(Magic mushroom, shroom)は、トリプタミン系アルカロイドのシロシビンやシロシンを含んだ、菌類のキノコの俗称。種は200以上存在し、世界中に広く自生している。毒キノコだが、主に幻覚作用であり重症や死亡はまずない。日本では『今昔物語』にて古代の呼び方で舞茸(今でいうマイタケとは違う)とされており、後世にも笑茸(わらいたけ)、踊茸などと言及された。多くが20世紀に菌類として同定され、大半はシビレタケ属 Psilocybe や糞を好むヒカゲタケ属 Panaeolus に属し、具体的な種はワライタケ、オオワライタケ、ヒカゲシビレタケ(日本原産)、ミナミシビレタケ (Psilocybe cubensis)、リバティーキャップ (Psilocybe Semilanceata) など。
日本では2002年から、シロシビンを含有するきのこ類を故意に使用・所持することは麻薬及び向精神薬取締法によって規制され、もっぱら鑑賞用となる。アメリカや日本では胞子の所持は合法である。21世紀に入り、成分シロシビンによるうつ病や薬物依存症の治療研究が注目され、日本でも強迫性障害に対するヒカゲシビレタケ抽出物が基礎研究された。
⇧ 日本でも、2002年までは、規制されてなかったんですね、いまは2022年だから、20年前か。研究結果次第では、医療用として規制緩和される日が来るんですかね?
総務省の資料によると、日本は世界でも自殺大国という位置付けらしいのですが...
(注)WHO資料においては、年間自殺者数及び自殺死亡率のデータについて、国により異なる年のデータが使用されており、我が国の年間自殺者数及び自殺死亡率については、厚生労働省の「平成 21 年人口動態統計」の数値が用いられている。
自殺の原因・動機が特定された2万 2,581 人について、原因・動機別にみると、健康問題が1万 4,621 人(64.7%)と最も多く、次いで経済・生活問題が 6,406 人(28.4%)、家庭問題が 4,547 人(20.1%)、勤務問題が 2,689人(11.9%)などとなっており、健康問題が6割以上を占めている(注2)。
また、この原因・動機別の自殺者数の順位は、表9のとおり、平成 10 年に自殺者が急増してからほぼ同様の傾向で推移している。
(注2)警察庁の自殺統計原票は、遺書等の自殺を裏付ける資料により明らかに推定できる原因・動機を3つまで計上することとされているため、原因・動機別の自殺者数の合計は原因・動機特定者数(2万 2,581 人)とは一致しない。
⇧ 健康問題が最も多いようですが、具体的なことは記載されてないので何とも言えないですが、精神的な影響も大きい気がしますかね、現代社会は未曾有の大ストレス社会と言っても過言ではない気がしますし...
冒頭から脱線しましたが、今回もAzureのサービスについてです。
「Azureアカウント」の作成なんかについては、
⇧ 前回の記事で行っています。
レッツトライ~。
Azure Communication ServicesのSDK(Software Development Kit)のJavaを利用するのに必要なもの
公式のドキュメントによると、
Prerequisites
- An Azure account with an active subscription. Create an account for free.
- Java Development Kit (JDK) version 8 or above.
- Apache Maven.
- A deployed Communication Services resource.
⇧ とあるのだけど、ビルドツールとして「Maven」以外を使うってことを考慮してくれてないのがよく分からん...
事前に、必要な要件が上記ということらしい。
「Azureアカウント」は作成済み、JDKはEclipse内蔵の使ってるし、ビルドツールは「Gradle」使ってるので、「Azure Commnunication Services 」のリソース作成が必要ってことになるのかな。
Azure Communication Servicesのリソース作成で
というわけで、「Azure Commnunication Services 」のリソースを作成で。
Azure Portalで「すべてのサービス」を選択。
「communication」的な用語で検索して、ヒットする「通信サービス」を選択。
「リソース の作成」ボタンを押下。
「基本情報」タブの入力が終わったら、「レビューと作成」タブを選択。「タグ」については割愛。
「作成」ボタンを押下。
「Azure Commnunication Services 」のリソースが作成されます。
「リソースに移動」ボタン押下。
確認できました。
Azure Communication Servicesを利用するのに必要な依存関係(Spring Boot使ってる場合)
Azure Communication Servicesを利用するのに必要な「依存関係」としては、
⇧ 上記の2つは最低限必要らしい。
ただ、手前味噌になり恐縮ですが、
⇧ 上記記事で、「Azure Commnunication Services」について調査した時に、「Azure Commnunication Services」のAPIを利用するための認証とかの関係で、
⇧ 上記の「依存関係」も共通で必要になる気がするんだけど、Microsoftさんのドキュメントが相変わらずカオスなので、よく分からんです...
今回はチャットを試してみたいので、
⇧ 上記の「依存関係」は必須になると思うけど。
Azure Communication ServicesのSDK(Software Development Kit)のJavaを試してみる
というわけで、試してみます。
フロントエンドについては、
⇧上記の記事で利用してたVue.jsのプロジェクトに追加してます。
サーバーサイドは新しく作ってます。
まずは、フロントエンド側。
⇧ 新規で追加したのは、3ファイル。
■C:\Users\Toshinobu\Desktop\soft_work\vue_work\vue-router-work\my-project-vue\src\modules\chat\chat.user.model.ts
interface ChatUserModelType { userId: string; userName: string; messageType: string; content: string; chatMessageId: any; chatThreadId: any; } class ChatUserModel implements ChatUserModelType { userId = ""; userName = ""; messageType = ""; content = ""; chatMessageId = ""; chatThreadId = ""; constructor(init?: Partial<chatusermodel>) { Object.assign(this, init); } } export default new ChatUserModel();
■C:\Users\Toshinobu\Desktop\soft_work\vue_work\vue-router-work\my-project-vue\src\api\chatService.ts
import request from "@/api/request"; class ChatService { sendMessage(data: any) { return request({ method: "post", url: "/chat/message", data: data, responseType: "json", }); } } export default new ChatService();
■C:\Users\Toshinobu\Desktop\soft_work\vue_work\vue-router-work\my-project-vue\src\views\chat\TestChat.vue
<template> <div id="chat"> <div id="chat-message"> <ul v-if="messageList && messageList.length"> <li v-for="item in messageList" :key="item.userId"> <div> <span> {{ item.content }}</span> <span> {{ item.chatThreadId }} </span> <span> {{ item.chatMessageId }} </span> </div> </li> </ul> </div> <div id="chat-form"> <el-form :model="formDataModel" ref="formDataModel"> <el-form-item label="メッセージ"> <el-input type="textarea" v-model="formDataModel.content"></el-input> </el-form-item> <button @click.prevent="sendMessage">送信</button> </el-form> </div> </div> </template> <script lang="ts"> /* eslint-disable no-console */ import { Component, Vue, Watch } from "vue-property-decorator"; import ChatUserModel from "@/modules/chat/chat.user.model"; import chatService from "@/api/chatService"; @Component export default class TestChatView extends Vue { private formDataModel: any = {}; private messageList: Array<typeof ChatUserModel> = []; private chatUserModel: any = {}; created() { console.log("created"); this.formDataModel = Object.assign({}, this.formDataModel, { userId: ChatUserModel.userId, userName: ChatUserModel.userName, messageType: ChatUserModel.messageType, content: ChatUserModel.content, chatMessageId: ChatUserModel.chatMessageId, chatThreadId: ChatUserModel.chatThreadId, }); //this.messageList.push(this.formDataModel); } private async sendMessage() { //this.messageList.push(this.formDataModel); //let formData = new FormData(); console.dir(this.formDataModel); //formData.append("chatUserDto", this.formDataModel); this.chatUserModel = Object.assign({}, this.formDataModel, { userId: this.formDataModel.userId, userName: this.formDataModel.userName, messageType: this.formDataModel.messageType, content: this.formDataModel.content, chatMessageId: this.formDataModel.chatMessageId, chatThreadId: this.formDataModel.chatThreadId, }); console.dir(this.chatUserModel); this.$set(this.formDataModel, "content", ""); try { await chatService.sendMessage(this.chatUserModel).then((result) => { console.log("await chatService.sendMessage"); console.dir(result.data); this.messageList.push({ userId: result.data.userId, userName: result.data.userName, messageType: result.data.messageType, content: result.data.content, chatMessageId: result.data.chatMessageId, chatThreadId: result.data.chatThreadId, }); }); } catch (error) { console.log(error); } } @Watch("formDataModel.content") private watchContent() { console.dir(this.formDataModel); } } </script>
■C:\Users\Toshinobu\Desktop\soft_work\vue_work\vue-router-work\my-project-vue\src\router\index.ts
import Vue from "vue"; import VueRouter, { RouteConfig } from "vue-router"; import HomeView from "../views/HomeView.vue"; Vue.use(VueRouter); const routes: Array<RouteConfig> = [ { path: "/", name: "home", component: HomeView, }, { path: "/about", name: "about", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ "../views/AboutView.vue"), }, // { // path: "/test", // name: "test", // component: () => import("../views/NavigationGuard.vue"), // }, // { // path: "/testdom", // name: "testdom", // component: () => import("../views/TestDom.vue"), // }, { path: "/testui", name: "testui", component: () => import("../views/TestElementUi.vue"), }, { path: "/user/list", component: () => import("../views/user/list.vue"), }, { path: "/user/edit/:id", name: "/user/edit", component: () => import("../views/user/edit/index.vue"), //props: (route) => ({ id: Number(route.params.id) }), }, { path: "/user/confirm", name: "/user/confirm", component: () => import("../views/user/confirm/index.vue"), //props: (route) => ({ id: Number(route.params.id) }), }, { path: "/chat", name: "chat", component: () => import("../views/chat/TestChat.vue"), }, ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); export default router;
⇧ で保存。その他は変更なし。
続きまして、サーバーサイド。
■azure-communication-services/build.gradle
plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator implementation 'org.springframework.boot:spring-boot-starter-actuator' // Azure Communication Services implementation 'com.azure:azure-communication-common:1.1.2' implementation 'com.azure:azure-sdk-bom:1.2.1' implementation 'com.azure:azure-communication-identity:1.1.8' implementation 'com.azure:azure-communication-chat:1.2.1' } tasks.named('test') { useJUnitPlatform() }
■azure-communication-services/src/main/resources/application.properties
server.port=8088 spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5434/test spring.datasource.username=postgres spring.datasource.password=postgres azure.communication.endpoint=[Azure Communication Servicesのエンドポイント] azure.communication.accessKey=[Azure Communication Servicesのアクセスキー] azure.communication.connection=endpoint=[Azure Communication Servicesの接続文字列]
⇧ Azure Communication ServicesへのAPI経由での接続は、
- エンドポイント+アクセスキー
- 接続文字列
のどちらかでOK。「接続文字列」が「エンドポイント」と「アクセスキー」を合わせたものになっているため。一応、application.propertiesに両方記載してますが、今回は「接続文字列」は使用してません。
設定する値は、Azure Portalで「Azure Communication Services」のリソースの「キー」で確認できます。
■azure-communication-services/src/main/java/com/example/demo/azure/communication/filter/CORSFilter.java
package com.example.demo.azure.communication.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; @Component public class CORSFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
■azure-communication-services/src/main/java/com/example/demo/azure/communication/dto/ChatUserDto.java
package com.example.demo.azure.communication.dto; import java.io.Serializable; import java.util.List; import com.azure.communication.chat.models.ChatMessageType; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @Data public class ChatUserDto implements Serializable { @JsonProperty("userId") private Long userId; @JsonProperty("userName") private String userName; //@JsonIgnore @JsonProperty("messageType") private ChatMessageType messageType; @JsonProperty("content") private String content; @JsonProperty("chatMessageId") private String chatMessageId; @JsonProperty("chatThreadId") private String chatThreadId; @JsonProperty("participantList") private ListparticipantList; }
■azure-communication-services/src/main/java/com/example/demo/azure/communication/auth/ApiAuth.java
package com.example.demo.azure.communication.auth; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.azure.communication.common.CommunicationUserIdentifier; import com.azure.communication.identity.CommunicationIdentityClient; import com.azure.communication.identity.CommunicationIdentityClientBuilder; import com.azure.communication.identity.models.CommunicationTokenScope; import com.azure.core.credential.AccessToken; import com.azure.core.credential.AzureKeyCredential; import lombok.Data; @Service @Data public class ApiAuth { @Value("${azure.communication.endpoint}") private String endpoint; @Value("${azure.communication.accessKey}") private String accessKey; public CommunicationIdentityClient authCommunicationServicesByIdentity () { CommunicationIdentityClient communicationIdentityClient = new CommunicationIdentityClientBuilder() .endpoint(endpoint) .credential(new AzureKeyCredential(accessKey)) .buildClient(); return communicationIdentityClient; } public CommunicationUserIdentifier createCommunicationServicesId (CommunicationIdentityClient client) { return client.createUser(); } public AccessToken createAccessTokenForCommunicationServices(CommunicationIdentityClient client) { List<CommunicationTokenScope> scopes = new ArrayList<>(Arrays.asList(CommunicationTokenScope.CHAT)); AccessToken accessToken = client.getToken(client.createUser(), scopes); return accessToken; } public AccessToken updateAccessTokenForCommunicationServices(CommunicationIdentityClient client, CommunicationUserIdentifier userIdentifier, List<CommunicationTokenScope> scopes) { CommunicationUserIdentifier identity = new CommunicationUserIdentifier(userIdentifier.getId()); AccessToken refreshToken = client.getToken(identity, scopes); return refreshToken; } public void revokeAccessToken(CommunicationIdentityClient client, CommunicationUserIdentifier userIdentifier) { client.revokeTokens(userIdentifier); } public void deleteAccessToken(CommunicationIdentityClient client, CommunicationUserIdentifier userIdentifier) { client.revokeTokens(userIdentifier); } }
■azure-communication-services/src/main/java/com/example/demo/azure/communication/service/CommunicationChatServiceImpl.java
package com.example.demo.azure.communication.service; import java.util.Objects; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import com.azure.communication.chat.ChatClient; import com.azure.communication.chat.ChatClientBuilder; import com.azure.communication.chat.ChatThreadClient; import com.azure.communication.chat.models.ChatMessageType; import com.azure.communication.chat.models.ChatParticipant; import com.azure.communication.chat.models.ChatThreadItem; import com.azure.communication.chat.models.CreateChatThreadOptions; import com.azure.communication.chat.models.CreateChatThreadResult; import com.azure.communication.chat.models.SendChatMessageOptions; import com.azure.communication.chat.models.SendChatMessageResult; import com.azure.communication.common.CommunicationTokenCredential; import com.azure.communication.common.CommunicationUserIdentifier; import com.azure.communication.identity.CommunicationIdentityClient; import com.azure.core.credential.AccessToken; import com.azure.core.http.rest.PagedIterable; import com.example.demo.azure.communication.auth.ApiAuth; import com.example.demo.azure.communication.dto.ChatUserDto; @Service public class CommunicationChatServiceImpl { @Autowired private ApiAuth apiAuth; public CommunicationTokenCredential createUserCredential() { CommunicationIdentityClient client = this.apiAuth.authCommunicationServicesByIdentity(); AccessToken userAccessToken = this.apiAuth.createAccessTokenForCommunicationServices(client); CommunicationTokenCredential userCredential = new CommunicationTokenCredential(userAccessToken.getToken()); return userCredential; } public CreateChatThreadOptions createChatThreadOptions(ChatUserDto ChatUserDto) { CommunicationIdentityClient client = this.apiAuth.authCommunicationServicesByIdentity(); CreateChatThreadOptions createChatThreadOptions = new CreateChatThreadOptions("Topic"); if (CollectionUtils.isEmpty(ChatUserDto.getParticipantList())) { String userId = null; if (Objects.isNull(ChatUserDto.getUserId())) { userId = client.createUser().getId(); } else { userId = String.valueOf(ChatUserDto.getUserId()); } ChatParticipant threadParticipant = new ChatParticipant() .setCommunicationIdentifier(new CommunicationUserIdentifier(userId)) .setDisplayName("participant-" + userId); createChatThreadOptions.addParticipant(threadParticipant); } else { for (String participantId: ChatUserDto.getParticipantList()) { ChatParticipant threadParticipant = new ChatParticipant() .setCommunicationIdentifier(new CommunicationUserIdentifier(participantId)) .setDisplayName("participant-" + participantId); createChatThreadOptions.addParticipant(threadParticipant); } } return createChatThreadOptions; } public ChatClient authCommunicationServicesByAcessToken() { CommunicationTokenCredential userCredential = this.createUserCredential(); final ChatClientBuilder builder = new ChatClientBuilder(); builder.endpoint(apiAuth.getEndpoint()) .credential(userCredential); ChatClient chatClient = builder.buildClient(); return chatClient; } public CreateChatThreadResult createChatThread(CreateChatThreadOptions createChatThreadOptions) { ChatClient chatClient = this.authCommunicationServicesByAcessToken(); CreateChatThreadResult createChatThreadResult = chatClient.createChatThread(createChatThreadOptions); return createChatThreadResult; } public PagedIterable<ChatThreadItem> showAllChatThreads(ChatClient chatClient) { return chatClient.listChatThreads(); } public ChatUserDto sendMessage(ChatUserDto chatUserDto, ChatClient chatClient) { SendChatMessageOptions sendChatMessageOptions = new SendChatMessageOptions() .setContent(chatUserDto.getContent()) // .setType(Objects.isNull(chatUserDto.getMessageType())? ChatMessageType.TEXT: // chatUserDto.getMessageType()) .setType(ChatMessageType.TEXT) .setSenderDisplayName(chatUserDto.getUserName()); ChatThreadClient chatThreadClient = chatClient.getChatThreadClient(chatUserDto.getChatThreadId()); SendChatMessageResult sendChatMessageResult = chatThreadClient.sendMessage(sendChatMessageOptions); String chatMessageId = sendChatMessageResult.getId(); chatUserDto.setChatMessageId(chatMessageId); chatThreadClient.sendReadReceipt(chatMessageId); return chatUserDto; } }
■azure-communication-services/src/main/java/com/example/demo/azure/communication/controller/ChatController.java
package com.example.demo.azure.communication.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.azure.communication.chat.ChatClient; import com.azure.communication.chat.models.ChatThreadProperties; import com.azure.communication.chat.models.CreateChatThreadOptions; import com.azure.communication.chat.models.CreateChatThreadResult; import com.example.demo.azure.communication.dto.ChatUserDto; import com.example.demo.azure.communication.service.CommunicationChatServiceImpl; import io.micrometer.core.instrument.util.StringUtils; @RestController @RequestMapping("/chat") public class ChatController { @Autowired private CommunicationChatServiceImpl communicationChatServiceImpl; @PostMapping("/message") public ChatUserDto message(@RequestBody ChatUserDto chatUserDto ) { ChatClient chatClient = this.communicationChatServiceImpl.authCommunicationServicesByAcessToken(); if (StringUtils.isEmpty(chatUserDto.getChatThreadId())) { //CommunicationTokenCredential communicationTokenCredential = this.communicationChatServiceImpl.createUserCredential(); CreateChatThreadOptions createChatThreadOptions = this.communicationChatServiceImpl.createChatThreadOptions(chatUserDto); CreateChatThreadResult createChatThreadResult = chatClient.createChatThread(createChatThreadOptions); //CreateChatThreadResult createChatThreadResult = this.communicationChatServiceImpl.createChatThread(createChatThreadOptions); ChatThreadProperties chatThreadProperties = createChatThreadResult.getChatThread(); chatUserDto.setChatThreadId(chatThreadProperties.getId()); } return communicationChatServiceImpl.sendMessage(chatUserDto, chatClient); } }
■azure-communication-services/src/main/java/com/example/demo/AzureCommunicationServicesApplication.java
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AzureCommunicationServicesApplication { public static void main(String[] args) { SpringApplication.run(AzureCommunicationServicesApplication.class, args); } }
⇧ 保存して、「フロントエンド」「サーバーサイド」のそれぞれのサーバーを起動。
ブラウザにアクセスし、Chatのページを開きます。
適当にメッセージを送信すると、メッセージが追加されていきます。
毎回、チャットスレッド作成してしまってるのを何とかしたい...何より、全くリアルタイム性が実現できずタイムラグが大きいのが致命的ですし...
データベースと連携できてなかったり、既読機能とかもできなかったり、他にも諸々できてないことが盛りだくさんになってますが、「Azure Communication Services」のSDK(Software Development Kit)のJavaを試すことができました。
相変わらず、Microsoftさんのドキュメント情報が足りてない...
おかげで、連休のほとんどが潰れたということは、言うまでもない...
今回はこのへんで。